"""Comprehensive OBD2 tests ported from deprecated Swift ECUmulator tests."""
from __future__ import annotations

from pymulator.core.uds import ServiceId

from .conftest import load_handler, make_message


def test_broadcast_pid_responds_with_supported_list():
    """Verify supported PIDs bitmap is returned correctly."""
    session = load_handler("obd2.json")
    resp = session.response_for_message(
        make_message(session.arbitration_id, [ServiceId.current_powertrain_diagnostics_data, 0x00])
    )
    assert resp[:2] == [0x41, 0x00]
    bitmap = int.from_bytes(bytes(resp[2:6]), "big")
    assert bitmap & (1 << (0x20 - 0x04))
    assert bitmap & (1 << (0x20 - 0x05))
    assert bitmap & (1 << (0x20 - 0x0C))
    assert bitmap & (1 << (0x20 - 0x0D))


def test_sequence_pid_cycles_through_responses():
    """Verify sequence PIDs cycle through their configured values."""
    session = load_handler("obd2.json")
    resp1 = session.response_for_message(
        make_message(session.arbitration_id, [ServiceId.current_powertrain_diagnostics_data, 0x05])
    )
    assert resp1 == [0x41, 0x05, 0x41]

    resp2 = session.response_for_message(
        make_message(session.arbitration_id, [ServiceId.current_powertrain_diagnostics_data, 0x05])
    )
    assert resp2 == [0x41, 0x05, 0x4B]

    resp3 = session.response_for_message(
        make_message(session.arbitration_id, [ServiceId.current_powertrain_diagnostics_data, 0x05])
    )
    assert resp3 == [0x41, 0x05, 0x46]

    resp4 = session.response_for_message(
        make_message(session.arbitration_id, [ServiceId.current_powertrain_diagnostics_data, 0x05])
    )
    assert resp4 == [0x41, 0x05, 0x41]


def test_dynamic_pid_uses_constant_generator():
    """Verify dynamic PID with constant generator returns correct value."""
    session = load_handler("obd2.json")
    resp = session.response_for_message(
        make_message(session.arbitration_id, [ServiceId.current_powertrain_diagnostics_data, 0x04])
    )
    assert resp[:2] == [0x41, 0x04]
    assert len(resp) == 3


def test_mode3_returns_stored_dtcs():
    """Verify Mode 3 returns stored (confirmed) DTCs."""
    session = load_handler("obd2.json")
    resp = session.response_for_message(
        make_message(session.arbitration_id, [ServiceId.emission_related_dtcs])
    )
    assert resp[0] == 0x43
    assert resp[1] >= 1


def test_mode7_reports_pending_dtcs():
    """Verify Mode 7 returns pending DTCs."""
    session = load_handler("obd2.json")
    resp = session.response_for_message(
        make_message(session.arbitration_id, [ServiceId.pending_emission_related_dtcs])
    )
    assert resp[0] == 0x47
    assert resp[1] >= 1


def test_mode4_clears_stored_and_pending_dtcs():
    """Verify Mode 4 clears both stored and pending DTCs."""
    session = load_handler("obd2.json")

    stored_before = session.response_for_message(
        make_message(session.arbitration_id, [ServiceId.emission_related_dtcs])
    )
    assert stored_before[1] >= 1

    pending_before = session.response_for_message(
        make_message(session.arbitration_id, [ServiceId.pending_emission_related_dtcs])
    )
    assert pending_before[1] >= 1

    clear = session.response_for_message(
        make_message(session.arbitration_id, [ServiceId.reset_emission_related_dtcs])
    )
    assert clear == [0x44]

    stored_after = session.response_for_message(
        make_message(session.arbitration_id, [ServiceId.emission_related_dtcs])
    )
    assert stored_after == [0x43, 0x00]

    pending_after = session.response_for_message(
        make_message(session.arbitration_id, [ServiceId.pending_emission_related_dtcs])
    )
    assert pending_after == [0x47, 0x00]


def test_informational_mode_returns_calibration_id():
    """Verify Mode 9 PID 04 returns calibration ID."""
    session = load_handler("obd2.json")
    resp = session.response_for_message(
        make_message(session.arbitration_id, [ServiceId.vehicle_information, 0x04])
    )
    assert resp[:2] == [0x49, 0x04]
    assert bytes(resp[2:]) == b"CAL01"


def test_supported_informational_pids_list_includes_defined_entries():
    """Verify Mode 9 PID 00 includes supported PIDs."""
    session = load_handler("obd2.json")
    resp = session.response_for_message(
        make_message(session.arbitration_id, [ServiceId.vehicle_information, 0x00])
    )
    assert resp[:2] == [0x49, 0x00]
    bitmap = int.from_bytes(bytes(resp[2:6]), "big")
    assert bitmap & (1 << (0x20 - 0x01))
    assert bitmap & (1 << (0x20 - 0x02))
    assert bitmap & (1 << (0x20 - 0x03))
    assert bitmap & (1 << (0x20 - 0x04))


def test_mode9_message_count_pids_are_computed_from_data_pid_payload_size():
    """Verify Mode 9 count PIDs (e.g. 0901/0903) are auto-derived from paired payload PIDs."""
    session = load_handler("obd2.json")

    vin_count = session.response_for_message(
        make_message(session.arbitration_id, [ServiceId.vehicle_information, 0x01])
    )
    assert vin_count == [0x49, 0x01, 0x03]

    calid_count = session.response_for_message(
        make_message(session.arbitration_id, [ServiceId.vehicle_information, 0x03])
    )
    assert calid_count == [0x49, 0x03, 0x01]


def test_unsupported_pid_produces_no_response():
    """Verify unsupported PIDs produce no response."""
    session = load_handler("obd2.json")
    message = make_message(session.arbitration_id, [ServiceId.current_powertrain_diagnostics_data, 0xFF])
    resp = session.handle(message, lambda _: None)
    assert resp is None


def test_vehicle_information_vin():
    """Verify Mode 9 PID 02 returns VIN."""
    session = load_handler("obd2.json")
    resp = session.response_for_message(
        make_message(session.arbitration_id, [ServiceId.vehicle_information, 0x02])
    )
    assert resp[:2] == [0x49, 0x02]
    vin_bytes = bytes(resp[2:])
    assert vin_bytes == b"TESTVIN123456789"


def test_freeze_frame_data_with_frame_index():
    """Verify Mode 2 returns freeze frame data with frame index (or no response if not configured)."""
    from pymulator.core.automotive import NoResponseFromBus
    session = load_handler("obd2.json")
    try:
        resp = session.response_for_message(
            make_message(session.arbitration_id, [ServiceId.powertrain_freeze_frame_data, 0x0C, 0x00])
        )
        if resp is not None:
            assert resp[0] == 0x42
            assert resp[1] == 0x0C
    except NoResponseFromBus:
        pass


def test_on_board_monitoring_supported_pids():
    """Verify Mode 6 PID 00 returns supported monitoring tests."""
    session = load_handler("obd2.json")
    resp = session.response_for_message(
        make_message(session.arbitration_id, [ServiceId.on_board_monitoring_test_results, 0x00])
    )
    assert resp[:2] == [0x46, 0x00]


def test_oxygen_sensor_monitoring_supported_pids():
    """Verify Mode 5 PID 00 returns supported O2 sensor tests."""
    session = load_handler("obd2.json")
    resp = session.response_for_message(
        make_message(session.arbitration_id, [ServiceId.oxygen_sensor_monitoring_test_results, 0x00])
    )
    assert resp[:2] == [0x45, 0x00]


def test_control_operation_returns_configured_response():
    """Verify Mode 8 control operation returns configured data."""
    session = load_handler("obd2.json")
    resp = session.response_for_message(
        make_message(session.arbitration_id, [ServiceId.control_on_board_system_test_or_component, 0x01])
    )
    assert resp == [0x48, 0x01, 0xAA]


def test_permanent_dtcs():
    """Verify Mode A returns permanent DTCs."""
    session = load_handler("obd2.json")
    resp = session.response_for_message(
        make_message(session.arbitration_id, [ServiceId.permanent_emission_related_dtcs])
    )
    assert resp[0] == 0x4A


def test_simple_pid_returns_configured_value():
    """Verify simple PIDs return their configured hex values."""
    session = load_handler("obd2.json")
    resp = session.response_for_message(
        make_message(session.arbitration_id, [ServiceId.current_powertrain_diagnostics_data, 0x0C])
    )
    assert resp == [0x41, 0x0C, 0x1A, 0xF8]

    resp2 = session.response_for_message(
        make_message(session.arbitration_id, [ServiceId.current_powertrain_diagnostics_data, 0x0D])
    )
    assert resp2 == [0x41, 0x0D, 0x28]
