import pytest
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.asymmetric.utils import encode_dss_signature

from aliro_harness.reader.auth1 import (
    build_auth1_apdu,
    build_auth1_data,
    build_table_812,
    build_table_813,
    sign_table_812_raw,
)


def test_table_812_layout_per_spec_table_8_12():
    t = build_table_812(
        reader_id_32=b"\x4d" * 32,
        credential_ephem_pub_x=b"\x86" * 32,
        reader_ephem_pub_x=b"\x87" * 32,
        transaction_id=b"\x4c" * 16,
    )
    assert t[0:2] == bytes([0x4D, 0x20])
    assert t[2:34] == b"\x4d" * 32
    assert t[34:36] == bytes([0x86, 0x20])
    assert t[36:68] == b"\x86" * 32
    assert t[68:70] == bytes([0x87, 0x20])
    assert t[70:102] == b"\x87" * 32
    assert t[102:104] == bytes([0x4C, 0x10])
    assert t[104:120] == b"\x4c" * 16
    assert t[120:122] == bytes([0x93, 0x04])
    assert t[122:126] == bytes.fromhex("415D9569")  # reader sig usage
    # 5 TLVs: 34+34+34+18+6 = 126 bytes (plan's "128" was off-by-2; byte
    # offsets above are self-consistent and match ReaderSide.buildTable812).
    assert len(t) == 126


def test_table_813_uses_ud_usage_tag():
    """Same TLV positions as 8-12, but the 0x93 value = UD usage tag."""
    kwargs = dict(
        reader_id_32=b"\x4d" * 32,
        credential_ephem_pub_x=b"\x86" * 32,
        reader_ephem_pub_x=b"\x87" * 32,
        transaction_id=b"\x4c" * 16,
    )
    t = build_table_813(**kwargs)
    t12 = build_table_812(**kwargs)
    # Everything up through the 0x93 0x04 header is identical...
    assert t[0:122] == t12[0:122]
    assert t[120:122] == bytes([0x93, 0x04])
    # ...but the usage tag differs.
    assert t[122:126] == bytes.fromhex("4E887B4C")
    assert t12[122:126] == bytes.fromhex("415D9569")
    assert len(t) == 126


def test_sign_then_verify_round_trip():
    """Sign Table 8-12 with a fresh P-256 key, verify with public half.
    Raw 64B r||s must re-assemble into a DER signature that verifies."""
    priv = ec.generate_private_key(ec.SECP256R1())
    table_812 = build_table_812(
        reader_id_32=b"\x01" * 32,
        credential_ephem_pub_x=b"\x02" * 32,
        reader_ephem_pub_x=b"\x03" * 32,
        transaction_id=b"\x04" * 16,
    )
    raw_sig = sign_table_812_raw(table_812, priv)
    assert len(raw_sig) == 64

    r = int.from_bytes(raw_sig[:32], "big")
    s = int.from_bytes(raw_sig[32:], "big")
    der = encode_dss_signature(r, s)
    # Should verify -- raises InvalidSignature if not.
    priv.public_key().verify(der, table_812, ec.ECDSA(hashes.SHA256()))


def test_build_auth1_data_with_cmd_params_zero():
    """cmd_params=0 ('key_slot' path) still emits the 0x41/0x9E TLV shell."""
    sig = b"\xaa" * 64
    data = build_auth1_data(sig, command_parameters=0x00)
    assert data[:5] == bytes([0x41, 0x01, 0x00, 0x9E, 0x40])
    assert data[5:69] == sig
    assert len(data) == 69


def test_build_auth1_data_rejects_wrong_sig_length():
    with pytest.raises(ValueError, match="64B raw"):
        build_auth1_data(b"\x00" * 63)


def test_build_auth1_apdu_short_form_wrap():
    """CLA=0x80, INS=0x81, P1=P2=0, Lc=len(data), then data."""
    data = bytes([0x41, 0x01, 0x01]) + bytes([0x9E, 0x40]) + b"\xcc" * 64
    apdu = build_auth1_apdu(data)
    assert apdu[0] == 0x80          # CLA
    assert apdu[1] == 0x81          # INS_AUTH1
    assert apdu[2] == 0x00          # P1
    assert apdu[3] == 0x00          # P2
    assert apdu[4] == len(data)     # Lc (short form)
    assert apdu[5:] == data
    assert len(apdu) == 5 + len(data)
