"""Fake serial interface for {{ interface_name }}.

Simulated serial port for testing without hardware.

Generated by: madsci new comm --template comm/serial
Template version: {{ template_version }}
"""

import logging
import time
from typing import Any

logger = logging.getLogger(__name__)


class {{ interface_name | pascal_case }}FakeInterface:
    """Fake serial interface for testing {{ interface_name }} without hardware.

    Simulates serial port behavior with configurable responses.

    Example:
        >>> interface = {{ interface_name | pascal_case }}FakeInterface()
        >>> interface.set_response(b"OK\\n")
        >>> interface.open()
        >>> interface.write(b"*IDN?\\n")
        >>> response = interface.read()
        >>> assert response == b"OK\\n"
    """

    def __init__(
        self,
        port: str = "/dev/ttyFAKE",
        baudrate: int = 9600,
        timeout: float = 1.0,
        simulated_latency: float = 0.01,
        **kwargs: Any,
    ) -> None:
        """Initialize the fake serial interface.

        Args:
            port: Simulated port path.
            baudrate: Simulated baud rate.
            timeout: Simulated read timeout.
            simulated_latency: Artificial delay for each operation in seconds.
            **kwargs: Additional configuration (ignored).
        """
        self._port = port
        self._baudrate = baudrate
        self._timeout = timeout
        self._simulated_latency = simulated_latency
        self._is_open = False
        self._write_buffer: list[bytes] = []
        self._read_buffer = b""
        self._operation_count = 0

    @property
    def is_open(self) -> bool:
        """Check if the port is open."""
        return self._is_open

    @property
    def operation_count(self) -> int:
        """Get the total number of read/write operations performed."""
        return self._operation_count

    @property
    def write_history(self) -> list[bytes]:
        """Get a copy of the write history."""
        return list(self._write_buffer)

    def set_response(self, data: bytes) -> None:
        """Set the data that will be returned by read/readline.

        Args:
            data: Bytes to enqueue in the read buffer.
        """
        self._read_buffer = data

    def open(self) -> None:
        """Simulate opening the serial port."""
        time.sleep(self._simulated_latency)
        self._is_open = True
        logger.info(f"[FAKE] Opened serial port {self._port}")

    def close(self) -> None:
        """Simulate closing the serial port."""
        if self._is_open:
            self._is_open = False
            logger.info(f"[FAKE] Closed serial port {self._port}")

    def write(self, data: bytes) -> int:
        """Simulate writing data to the serial port.

        Args:
            data: Bytes to write.

        Returns:
            Number of bytes written.

        Raises:
            RuntimeError: If port is not open.
        """
        if not self._is_open:
            raise RuntimeError("Serial port not open. Call open() first.")
        time.sleep(self._simulated_latency)
        self._write_buffer.append(data)
        self._operation_count += 1
        return len(data)

    def read(self, size: int = 1024) -> bytes:
        """Simulate reading data from the serial port.

        Args:
            size: Maximum number of bytes to read.

        Returns:
            Bytes from the internal read buffer.

        Raises:
            RuntimeError: If port is not open.
        """
        if not self._is_open:
            raise RuntimeError("Serial port not open. Call open() first.")
        time.sleep(self._simulated_latency)
        self._operation_count += 1
        result = self._read_buffer[:size]
        self._read_buffer = self._read_buffer[size:]
        return result

    def readline(self) -> bytes:
        """Simulate reading a line from the serial port.

        Returns:
            Bytes up to and including the first newline, or all remaining bytes.

        Raises:
            RuntimeError: If port is not open.
        """
        if not self._is_open:
            raise RuntimeError("Serial port not open. Call open() first.")
        time.sleep(self._simulated_latency)
        self._operation_count += 1
        idx = self._read_buffer.find(b"\n")
        if idx >= 0:
            result = self._read_buffer[: idx + 1]
            self._read_buffer = self._read_buffer[idx + 1 :]
            return result
        result = self._read_buffer
        self._read_buffer = b""
        return result

    def flush(self) -> None:
        """Simulate flushing buffers.

        Raises:
            RuntimeError: If port is not open.
        """
        if not self._is_open:
            raise RuntimeError("Serial port not open. Call open() first.")

    def query(self, command: str, terminator: str = "\n") -> str:
        """Simulate query (write command, then readline response).

        Args:
            command: Command string to send.
            terminator: Line terminator to append.

        Returns:
            Decoded and stripped response string.

        Raises:
            RuntimeError: If port is not open.
        """
        if not self._is_open:
            raise RuntimeError("Serial port not open. Call open() first.")
        self.write((command + terminator).encode())
        response = self.readline()
        return response.decode().strip()

    def reset(self) -> None:
        """Reset all internal state for test isolation."""
        self._write_buffer = []
        self._read_buffer = b""
        self._operation_count = 0
