from lr_modbus import cli
from lr_modbus.codec import build_frame
from lr_modbus.transport import SerialTimeoutError


class CliTransport:
    def __init__(self, responses=None):
        self.responses = list(responses or [])
        self.requests = []
        self.written = []

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc, tb):
        return False

    def open(self):  # pragma: no cover - compatibility shim
        return self

    def close(self):  # pragma: no cover
        return None

    def transact(self, payload: bytes, *, deadline=None) -> bytes:
        self.requests.append(payload)
        if not self.responses:
            raise AssertionError("No response queued")
        return self.responses.pop(0)

    def read_frame(self) -> bytes:
        raise SerialTimeoutError("no frame queued")

    def write_frame(self, payload: bytes) -> None:
        self.written.append(payload)


def test_cli_client_read(capsys) -> None:
    response = build_frame(1, 3, bytes([4, 0x00, 0x05, 0x00, 0x06]))
    transport = CliTransport([response])
    exit_code = cli.run_cli(
        [
            "--port",
            "/dev/ttyUSB0",
            "client",
            "--action",
            "read-holding",
            "--address",
            "0",
            "--count",
            "2",
        ],
        transport_factory=lambda config: transport,
    )
    assert exit_code == 0
    out = capsys.readouterr().out
    assert "holding[0] = 5" in out
    assert "holding[1] = 6" in out


def test_cli_server_listen_only() -> None:
    transport = CliTransport()
    exit_code = cli.run_cli(
        [
            "--port",
            "/dev/ttyUSB0",
            "server",
            "--listen-only",
            "--stop-after",
            "0.01",
        ],
        transport_factory=lambda config: transport,
    )
    assert exit_code == 0
