from __future__ import annotations

from pymulator.core.isotp import FlowStatus, ISOTPFDTransceiver, ISOTPTransceiver


def _pad(frame: list[int], length: int = 8) -> list[int]:
    return frame + [0x00] * (length - len(frame))


def test_isotp_respects_block_size_and_separation_time():
    tx = ISOTPTransceiver()
    rx = ISOTPTransceiver(block_size=2, separation_time_ms=7)
    payload = list(range(25))

    action = tx.write(payload)
    assert action.kind == "write_frames"
    assert action.frames

    ff_frame = _pad(action.frames[0])
    fc_action = rx.did_read(ff_frame)
    assert fc_action.kind == "write_frames"
    assert fc_action.frames
    assert fc_action.frames[0][:3] == [FlowStatus.clear_to_send, 2, 7]

    fc_frame = _pad(fc_action.frames[0])
    cf_action = tx.did_read(fc_frame)
    assert cf_action.kind == "write_frames"
    assert cf_action.separation_time == 7
    assert cf_action.frames
    assert len(cf_action.frames) == 2

    last_action = None
    for frame in cf_action.frames:
        last_action = rx.did_read(_pad(frame))

    assert last_action is not None
    assert last_action.kind == "write_frames"
    assert last_action.frames
    assert last_action.frames[0][0] == FlowStatus.clear_to_send


def test_isotp_flow_control_wait_and_overflow():
    tx = ISOTPTransceiver()
    payload = list(range(20))
    action = tx.write(payload)
    assert action.kind == "write_frames"
    assert tx.state == "sending"

    wait_fc = _pad([FlowStatus.wait, 0x00, 0x00])
    wait_action = tx.did_read(wait_fc)
    assert wait_action.kind == "wait"
    assert tx.state == "sending"

    overflow_fc = _pad([FlowStatus.overflow, 0x00, 0x00])
    overflow_action = tx.did_read(overflow_fc)
    assert overflow_action.kind == "wait"
    assert tx.state == "idle"


def test_isotp_fd_single_frame_uses_two_byte_sf_header_for_payload_above_7_bytes():
    tx = ISOTPFDTransceiver()
    payload = list(range(12))

    action = tx.write(payload)

    assert action.kind == "write_frames"
    assert action.frames
    frame = action.frames[0]
    assert frame[0] == 0x00
    assert frame[1] == len(payload)
    assert frame[2:2 + len(payload)] == payload


def test_isotp_fd_multiframe_roundtrip():
    tx = ISOTPFDTransceiver()
    rx = ISOTPFDTransceiver(block_size=2, separation_time_ms=5)
    payload = list(range(70))

    first = tx.write(payload)
    assert first.kind == "write_frames"
    assert first.frames

    fc_action = rx.did_read(first.frames[0])
    assert fc_action.kind == "write_frames"
    assert fc_action.frames
    assert fc_action.frames[0][0] & 0xF0 == 0x30

    cf_action = tx.did_read(fc_action.frames[0])
    assert cf_action.kind == "write_frames"
    assert cf_action.frames

    completed = None
    for frame in cf_action.frames:
        completed = rx.did_read(frame)
    assert completed is not None
    assert completed.kind == "process"
    assert completed.bytes == payload
