from __future__ import annotations

import time

from pymulator.core.can import CANFrame
from pymulator.core.isotp import FlowStatus, ISOTPTransceiver

from .conftest import attach_ecu, bus_and_client, load_handler


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


def _next_frame(client, timeout: float = 0.5):
    try:
        return client.frames.get(timeout=timeout)
    except Exception as exc:  # pragma: no cover - safety net for queue.Empty on CI
        raise AssertionError("Timed out waiting for CAN frame") from exc


def test_ecu_flow_control_wait_then_cts(bus_and_client):
    bus, client = bus_and_client
    session = load_handler("uds.json")
    ecu = attach_ecu(bus, "UDS", session)
    ecu.configure_isotp_flow_control(wait_count=1, wait_ms=0.0, overflow=False)

    tx = ISOTPTransceiver()
    action = tx.write(list(range(20)))
    ff = action.frames[0]
    bus.write(CANFrame(arbitration_id=session.arbitration_id, data=_pad(ff), dlc=8), originator=client)

    fc_wait = _next_frame(client)
    assert fc_wait.arbitration_id == session.reply_id
    assert (fc_wait.data[0] & 0xF0) == 0x30
    assert fc_wait.data[0] == FlowStatus.wait

    fc_cts = _next_frame(client)
    assert fc_cts.arbitration_id == session.reply_id
    assert fc_cts.data[0] == FlowStatus.clear_to_send


def test_ecu_flow_control_wait_probability(bus_and_client):
    bus, client = bus_and_client
    session = load_handler("uds.json")
    ecu = attach_ecu(bus, "UDS", session)
    ecu.configure_isotp_flow_control(wait_count=0, wait_ms=0.0, wait_probability=1.0, seed=1)

    tx = ISOTPTransceiver()
    action = tx.write(list(range(20)))
    ff = action.frames[0]
    bus.write(CANFrame(arbitration_id=session.arbitration_id, data=_pad(ff), dlc=8), originator=client)

    fc_wait = _next_frame(client)
    assert fc_wait.arbitration_id == session.reply_id
    assert fc_wait.data[0] == FlowStatus.wait

    fc_cts = _next_frame(client)
    assert fc_cts.arbitration_id == session.reply_id
    assert fc_cts.data[0] == FlowStatus.clear_to_send
