from lr_modbus.codec import build_frame, parse_frame
from lr_modbus.server import InMemoryDataModel, ModbusServer


class NullTransport:
    def __enter__(self):  # pragma: no cover - used for interface completeness
        return self

    def __exit__(self, exc_type, exc, tb):  # pragma: no cover
        return False

    def read_frame(self):  # pragma: no cover
        raise NotImplementedError

    def write_frame(self, payload):  # pragma: no cover
        raise NotImplementedError


def test_server_handles_read_request() -> None:
    model = InMemoryDataModel({0: 1, 1: 2})
    server = ModbusServer(NullTransport(), unit_id=1, data_model=model)

    request = build_frame(1, 3, b"\x00\x00\x00\x02")
    response = server.handle_raw_frame(request)
    assert response is not None
    parsed = parse_frame(response)
    assert parsed.payload[0] == 4
    assert parsed.payload[1:] == b"\x00\x01\x00\x02"


def test_server_listen_only_mode() -> None:
    server = ModbusServer(NullTransport(), unit_id=1, listen_only=True)
    request = build_frame(1, 6, b"\x00\x01\x00\x02")
    assert server.handle_raw_frame(request) is None


def test_server_processes_broadcast_write_without_response() -> None:
    model = InMemoryDataModel()
    server = ModbusServer(NullTransport(), unit_id=1, data_model=model)
    broadcast = build_frame(0, 6, b"\x00\x05\x00\x2a")
    assert server.handle_raw_frame(broadcast) is None
    # Broadcast should update the register map even though no response is emitted
    assert model.read_holding_registers(5, 1)[0] == 0x2A
