from typing import ByteString


def encode_apdu_command(
    cla: int,
    ins: int,
    p1: int,
    p2: int,
    data: ByteString,
    expected_length: int,
    allow_extended: bool,
) -> bytearray:
    """
    Encode an APDU command.

    If allow_extended is not set, data must be at most 255 bytes and expected_length at most 256.
    Otherwise, data can be up to 65535 bytes and expected_length can be 65536.

    Args:
        cla: class byte
        ins: instruction byte
        p1: first parameter byte
        p2: second parameter byte
        data: command data to transmit
        expected_length: expected length
        allow_extended: extended length allowed

    Returns:
        Encoded APDU

    Raises:
        ValueError: data or expected_length too long.
    """

    max_len = 65536 if allow_extended else 256

    if len(data) >= max_len:
        raise ValueError(f"Data too long: {len(data)}")

    if expected_length > max_len:
        raise ValueError(f"Expected length too long: {expected_length}")

    buf = bytearray((cla, ins, p1, p2))

    if len(data) < 256 and expected_length <= 256:
        # short fields
        if data:
            buf.append(len(data))
            buf.extend(data)
        if expected_length:
            buf.append(expected_length & 0xFF)
    else:
        # long fields
        if data:
            buf.extend((0, len(data) >> 8, len(data) & 0xFF))
            buf.extend(data)
        if expected_length:
            if not data:
                buf.append(0)
            if expected_length == 65536:
                buf.extend((0, 0))
            else:
                buf.extend((expected_length >> 8, expected_length & 0xFF))
    return buf