from __future__ import annotations

from dataclasses import dataclass

from matterlab_serial_device.transport import SerialTransport, SerialTransportConfig


@dataclass
class SartoriusSimState:
    weight: float = 0.0
    stable: bool = True
    units: str = "g"


class StatefulBalanceTransport(SerialTransport):
    def __init__(self, config: SerialTransportConfig, protocol) -> None:
        self.config = config
        self._protocol = protocol
        self._is_open = True
        self._pending_response = b""
        self.write_history: list[bytes] = []

    @property
    def is_open(self) -> bool:
        return self._is_open

    def open(self) -> None:
        self._is_open = True

    def close(self) -> None:
        self._is_open = False

    def write(self, data: bytes) -> None:
        self.write_history.append(data)
        self._pending_response = self._protocol(data)

    def read_until(self, expected: bytes = b"\n", size: int | None = None) -> bytes:
        del expected
        response = self._pending_response
        self._pending_response = b""
        if size is not None:
            response = response[:size]
        return response


class SartoriusSimProtocol:
    def __init__(self, state: SartoriusSimState) -> None:
        self.state = state

    def __call__(self, write_data: bytes) -> bytes:
        if write_data == b"\x1bP\r\n":
            sign = "-" if self.state.weight < 0 else ""
            magnitude = abs(self.state.weight)
            if self.state.stable:
                payload = f"x {sign} {magnitude:.3f} {self.state.units}\r\n" if sign else f"x {magnitude:.3f} {self.state.units}\r\n"
            else:
                payload = f"x {sign} {magnitude:.3f}\r\n" if sign else f"x {magnitude:.3f}\r\n"
            return payload.encode("utf-8")
        if write_data == b"\x1bT\r\n":
            self.state.weight = 0.0
            self.state.stable = True
            return b""
        return b""


def create_sartorius_sim_transport_factory(*, weight: float = 0.0, stable: bool = True, units: str = "g"):
    state = SartoriusSimState(weight=weight, stable=stable, units=units)
    protocol = SartoriusSimProtocol(state)

    def _factory(config: SerialTransportConfig):
        return StatefulBalanceTransport(config, protocol)

    _factory.state = state
    return _factory
