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

import pytest

from pymulator.core.automotive import UdsNegativeResponse
from pymulator.core.uds import NegativeResponseCode, ServiceId

from .conftest import load_handler, make_message


def test_tester_present():
    """Verify KWP tester present returns positive response."""
    session = load_handler("kwp.json")
    resp = session.handle_request(
        make_message(session.arbitration_id, [ServiceId.tester_present])
    )
    assert resp[0] == 0x7E


def test_read_memory_by_address():
    """Verify KWP read memory by address works correctly."""
    session = load_handler("kwp.json")
    resp = session.handle_request(
        make_message(session.arbitration_id, [ServiceId.read_memory_by_address, 0x00, 0x20, 0x00, 0x04])
    )
    assert resp[0] == 0x63
    assert len(resp) == 5


def test_read_memory_rejects_zero_length():
    """Verify KWP read memory with zero length is rejected."""
    session = load_handler("kwp.json")
    with pytest.raises(UdsNegativeResponse) as exc_info:
        session.handle_request(
            make_message(session.arbitration_id, [ServiceId.read_memory_by_address, 0x00, 0x20, 0x00, 0x00])
        )
    assert exc_info.value.code == NegativeResponseCode.request_out_of_range


def test_start_diagnostic_session():
    """Verify KWP start diagnostic session returns session type only."""
    session = load_handler("kwp.json")
    resp = session.handle_request(
        make_message(session.arbitration_id, [ServiceId.diagnostic_session_control, 0x89])
    )
    assert resp[0] == 0x50
    assert resp[1] == 0x89


def test_ecu_reset():
    """Verify KWP ECU reset returns positive response."""
    session = load_handler("kwp.json")
    resp = session.handle_request(
        make_message(session.arbitration_id, [ServiceId.ecu_reset, 0x01])
    )
    assert resp == [0x51, 0x01]


def test_clear_diagnostic_information():
    """Verify KWP clear diagnostic information works."""
    session = load_handler("kwp.json")
    dtc_before = session.handle_request(
        make_message(session.arbitration_id, [ServiceId.kwp_read_diagnostic_trouble_code])
    )
    assert dtc_before[1] >= 1

    clear = session.handle_request(
        make_message(session.arbitration_id, [ServiceId.clear_diagnostic_information, 0xFF, 0xFF])
    )
    assert clear == [0x54]

    dtc_after = session.handle_request(
        make_message(session.arbitration_id, [ServiceId.kwp_read_diagnostic_trouble_code])
    )
    assert dtc_after == [0x53, 0x00]


def test_security_access_seed_and_key():
    """Verify KWP security access seed/key flow."""
    session = load_handler("kwp.json")
    session.handle_request(
        make_message(session.arbitration_id, [ServiceId.diagnostic_session_control, 0x89])
    )

    seed = session.handle_request(
        make_message(session.arbitration_id, [ServiceId.security_access, 0x01])
    )
    assert seed == [0x67, 0x01, 0x11]

    key = session.handle_request(
        make_message(session.arbitration_id, [ServiceId.security_access, 0x02, 0x22])
    )
    assert key == [0x67, 0x02]


def test_write_data_by_identifier():
    """Verify KWP write common identifier works."""
    session = load_handler("kwp.json")
    write = session.handle_request(
        make_message(session.arbitration_id, [ServiceId.write_data_by_identifier, 0xF1, 0x87, 0x99, 0x88])
    )
    assert write == [0x6E, 0xF1, 0x87]

    read = session.handle_request(
        make_message(session.arbitration_id, [ServiceId.read_data_by_identifier, 0xF1, 0x87])
    )
    assert read == [0x62, 0xF1, 0x87, 0x99, 0x88]


def test_routine_control_unsupported():
    """Verify KWP routine control returns error for unsupported routine."""
    session = load_handler("kwp.json")
    with pytest.raises(UdsNegativeResponse) as exc_info:
        session.handle_request(
            make_message(session.arbitration_id, [ServiceId.routine_control, 0x01, 0x00, 0x01])
        )
    assert exc_info.value.code == NegativeResponseCode.request_out_of_range


def test_request_download():
    """Verify KWP request download initiates transfer."""
    session = load_handler("kwp.json")
    session.handle_request(
        make_message(session.arbitration_id, [ServiceId.diagnostic_session_control, 0x89])
    )

    download = session.handle_request(
        make_message(session.arbitration_id, [ServiceId.request_download, 0x00, 0x22, 0x20, 0x00, 0x00, 0x04])
    )
    assert download[0] == 0x74


def test_dynamically_define_data_identifier():
    """Verify KWP dynamically define data identifier by existing identifier works."""
    session = load_handler("kwp.json")
    define = session.handle_request(
        make_message(
            session.arbitration_id,
            [ServiceId.dynamically_define_data_identifier, 0x01, 0xF3, 0x00, 0xF1, 0x87, 0x01, 0x01]
        )
    )
    assert define == [0x6C, 0x01, 0xF3, 0x00]

    read = session.handle_request(
        make_message(session.arbitration_id, [ServiceId.read_data_by_identifier, 0xF3, 0x00])
    )
    assert read == [0x62, 0xF3, 0x00, 0x10]


def test_read_scaling_data_by_identifier():
    """Verify KWP read scaling data returns same as common identifier."""
    session = load_handler("kwp.json")
    scaling = session.handle_request(
        make_message(session.arbitration_id, [ServiceId.read_scaling_data_by_identifier, 0xF1, 0x87])
    )
    assert scaling == [0x64, 0xF1, 0x87, 0x10, 0x11]


def test_ecu_identification_vin():
    """Verify KWP ECU identification can return VIN if configured."""
    session = load_handler("kwp.json")
    resp = session.handle_request(
        make_message(session.arbitration_id, [ServiceId.kwp_read_ecu_identification, 0x90])
    )
    assert resp[0] == 0x5A
    assert resp[1] == 0x90


def test_local_identifier():
    """Verify KWP local identifier read works."""
    session = load_handler("kwp.json")
    resp = session.handle_request(
        make_message(session.arbitration_id, [ServiceId.kwp_read_data_by_local_identifier, 0x01])
    )
    assert resp == [0x61, 0x01, 0xAA, 0xBB]


def test_freeze_frame_data():
    """Verify KWP freeze frame data returns empty response."""
    session = load_handler("kwp.json")
    resp = session.handle_request(
        make_message(session.arbitration_id, [ServiceId.kwp_read_freeze_frame_data])
    )
    assert resp == [0x52]


def test_read_dtcs():
    """Verify KWP read DTCs returns stored DTCs."""
    session = load_handler("kwp.json")
    resp = session.handle_request(
        make_message(session.arbitration_id, [ServiceId.kwp_read_diagnostic_trouble_code])
    )
    assert resp[0] == 0x53
    assert resp[1] >= 0


def test_read_status_of_dtcs():
    """Verify KWP read status of DTCs works."""
    session = load_handler("kwp.json")
    resp = session.handle_request(
        make_message(session.arbitration_id, [ServiceId.kwp_read_status_of_dtcs, 0xFF])
    )
    assert resp[0] == 0x57


def test_read_dtcs_by_status():
    """Verify KWP read DTCs by status mask works."""
    session = load_handler("kwp.json")
    resp = session.handle_request(
        make_message(session.arbitration_id, [ServiceId.kwp_read_dtcs_by_status, 0x02, 0xFF, 0xFF])
    )
    assert resp[0] == 0x58


def test_stop_diagnostic_session():
    """Verify KWP stop diagnostic session resets to default."""
    session = load_handler("kwp.json")
    session.handle_request(
        make_message(session.arbitration_id, [ServiceId.diagnostic_session_control, 0x89])
    )

    stop = session.handle_request(
        make_message(session.arbitration_id, [ServiceId.kwp_stop_diagnostic_session])
    )
    assert stop == [0x60]


def test_start_communication():
    """Verify KWP start communication echoes payload."""
    session = load_handler("kwp.json")
    resp = session.handle_request(
        make_message(session.arbitration_id, [ServiceId.kwp_start_communication, 0x12, 0x34])
    )
    assert resp == [0xC1, 0x12, 0x34]


def test_stop_communication():
    """Verify KWP stop communication echoes empty payload."""
    session = load_handler("kwp.json")
    resp = session.handle_request(
        make_message(session.arbitration_id, [ServiceId.kwp_stop_communication])
    )
    assert resp == [0xC2]


def test_access_timing_parameters():
    """Verify KWP access timing parameters returns stored timing values."""
    session = load_handler("kwp.json")
    resp = session.handle_request(
        make_message(session.arbitration_id, [ServiceId.kwp_access_timing_parameters, 0x01])
    )
    assert resp == [0xC3, 0x01, 0x00, 0x00, 0x00]

    updated = session.handle_request(
        make_message(session.arbitration_id, [ServiceId.kwp_access_timing_parameters, 0x02, 0x12, 0x34, 0x56])
    )
    assert updated == [0xC3, 0x02]

    resp2 = session.handle_request(
        make_message(session.arbitration_id, [ServiceId.kwp_access_timing_parameters, 0x01])
    )
    assert resp2 == [0xC3, 0x01, 0x12, 0x34, 0x56]


def test_network_configuration():
    """Verify KWP network configuration echoes payload."""
    session = load_handler("kwp.json")
    resp = session.handle_request(
        make_message(session.arbitration_id, [ServiceId.kwp_network_configuration, 0xAA])
    )
    assert resp == [0xC4, 0xAA]
