"""Fake interface for {{ module_name }} instrument.

This module provides a simulated interface for testing without hardware.
It implements the same methods as the real interface but operates entirely
in memory, with configurable latency and full state tracking for testing.

Generated by: madsci new module --template module/instrument
Template version: {{ template_version }}
"""

import logging
import random
import time
from typing import Any, Optional

from {{ module_name }}_types import (
    {{ module_name | pascal_case }}CalibrationData,
    {{ module_name | pascal_case }}InterfaceConfig,
    {{ module_name | pascal_case }}Reading,
)

logger = logging.getLogger(__name__)


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

    This class simulates instrument behavior including measurement,
    calibration, and lifecycle management.

    Example:
        >>> interface = {{ module_name | pascal_case }}FakeInterface(simulated_latency=0.01)
        >>> interface.connect()
        >>> interface.initialize()
        >>> reading = interface.measure()
        >>> interface.disconnect()
    """

    def __init__(
        self,
        config: Optional[{{ module_name | pascal_case }}InterfaceConfig] = None,
        simulated_latency: float = 0.1,
        **kwargs: Any,
    ) -> None:
        """Initialize the fake interface.

        Args:
            config: Interface configuration. Uses defaults if None.
            simulated_latency: Simulated operation time in seconds.
            **kwargs: Additional configuration (ignored in fake mode).
        """
        self._config = config or {{ module_name | pascal_case }}InterfaceConfig(**kwargs)
        self._simulated_latency = simulated_latency
        self._connected = False
        self._initialized = False
        self._calibration = {{ module_name | pascal_case }}CalibrationData()
        self._operation_count = 0
        self._readings: list[{{ module_name | pascal_case }}Reading] = []
        self._state: dict[str, Any] = {}

    @property
    def is_connected(self) -> bool:
        """Check if the interface is connected."""
        return self._connected

    @property
    def is_initialized(self) -> bool:
        """Check if the instrument has been initialized."""
        return self._initialized

    @property
    def is_calibrated(self) -> bool:
        """Check if the instrument is calibrated."""
        return self._calibration.calibrated

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

    @property
    def readings(self) -> list[{{ module_name | pascal_case }}Reading]:
        """Get the history of readings taken."""
        return list(self._readings)

    def connect(self) -> None:
        """Simulate establishing connection to the instrument."""
        logger.info("[FAKE] Connecting to simulated {{ module_name }} instrument...")
        time.sleep(self._simulated_latency)
        self._connected = True
        self._state["connected_at"] = time.time()
        logger.info("[FAKE] Connected to simulated {{ module_name }} instrument")

    def disconnect(self) -> None:
        """Simulate closing connection to the instrument."""
        if self._connected:
            logger.info("[FAKE] Disconnecting from simulated {{ module_name }} instrument...")
            self._connected = False
            self._initialized = False
            self._state["disconnected_at"] = time.time()
            logger.info("[FAKE] Disconnected from simulated {{ module_name }} instrument")

    def initialize(self) -> None:
        """Simulate instrument initialization.

        Raises:
            RuntimeError: If not connected.
        """
        if not self._connected:
            raise RuntimeError("Not connected. Call connect() first.")

        logger.info("[FAKE] Initializing simulated {{ module_name }} instrument...")
        time.sleep(self._simulated_latency)
        self._initialized = True
        self._state["initialized_at"] = time.time()
        logger.info("[FAKE] Simulated {{ module_name }} instrument initialized")

    def shutdown(self) -> None:
        """Simulate instrument shutdown.

        Raises:
            RuntimeError: If not connected.
        """
        if not self._connected:
            raise RuntimeError("Not connected. Call connect() first.")

        logger.info("[FAKE] Shutting down simulated {{ module_name }} instrument...")
        time.sleep(self._simulated_latency)
        self._initialized = False
        self._state["shutdown_at"] = time.time()
        logger.info("[FAKE] Simulated {{ module_name }} instrument shut down")

    def calibrate(self, **kwargs: Any) -> {{ module_name | pascal_case }}CalibrationData:
        """Simulate instrument calibration.

        Args:
            **kwargs: Calibration parameters.

        Returns:
            CalibrationData with simulated calibration results.

        Raises:
            RuntimeError: If not connected or not initialized.
        """
        if not self._connected:
            raise RuntimeError("Not connected. Call connect() first.")
        if not self._initialized:
            raise RuntimeError("Instrument not initialized. Call initialize() first.")

        time.sleep(self._simulated_latency)
        self._operation_count += 1
        self._calibration = {{ module_name | pascal_case }}CalibrationData(
            calibrated=True,
            calibration_timestamp=time.time(),
            calibration_parameters=kwargs,
        )
        logger.info("[FAKE] Simulated calibration complete")
        return self._calibration

    def measure(self, channel: Optional[str] = None) -> {{ module_name | pascal_case }}Reading:
        """Simulate taking a measurement.

        Args:
            channel: Optional channel or sensor to read from.

        Returns:
            Reading with a simulated value.

        Raises:
            RuntimeError: If not connected or not initialized.
        """
        if not self._connected:
            raise RuntimeError("Not connected. Call connect() first.")
        if not self._initialized:
            raise RuntimeError("Instrument not initialized. Call initialize() first.")

        time.sleep(self._simulated_latency)
        self._operation_count += 1

        reading = {{ module_name | pascal_case }}Reading(
            value=round(random.uniform(0.0, 100.0), 4),
            units="counts",
            timestamp=time.time(),
            channel=channel,
        )
        self._readings.append(reading)
        self._state["last_reading"] = reading.value

        logger.info(f"[FAKE] Simulated measurement: {reading.value} {reading.units}")
        return reading

    def get_status(self) -> dict:
        """Get the simulated instrument status.

        Returns:
            dict: Simulated instrument status information.

        Raises:
            RuntimeError: If not connected.
        """
        if not self._connected:
            raise RuntimeError("Not connected. Call connect() first.")

        return {
            "connected": self._connected,
            "initialized": self._initialized,
            "calibrated": self._calibration.calibrated,
            "status_code": 0,
            "operation_count": self._operation_count,
            "readings_count": len(self._readings),
        }

    def get_state(self) -> dict[str, Any]:
        """Get the current internal state (useful for testing).

        Returns:
            Dictionary of internal state.
        """
        return {
            "connected": self._connected,
            "initialized": self._initialized,
            "calibrated": self._calibration.calibrated,
            "operation_count": self._operation_count,
            "readings_count": len(self._readings),
            **self._state,
        }

    def reset_state(self) -> None:
        """Reset internal state (useful for testing)."""
        self._operation_count = 0
        self._readings = []
        self._calibration = {{ module_name | pascal_case }}CalibrationData()
        self._state = {}
