"""Interface for {{ module_name }} camera.

This module provides the hardware interface for communicating with
the {{ module_name }} camera. The interface includes full device lifecycle
management: connect, initialize, shutdown, disconnect, and image capture.

The interface is independent of MADSci and can be used directly in
notebooks or scripts.

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

import logging
import time
from typing import Any, Optional

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

logger = logging.getLogger(__name__)


class {{ module_name | pascal_case }}Interface:
    """Interface for communicating with the {{ module_name }} camera.

    This class encapsulates all hardware communication logic, including
    device lifecycle management (connect, initialize, shutdown, disconnect),
    image capture, and camera configuration.

    It can be used independently of MADSci in notebooks or scripts.

    Example:
        >>> config = {{ module_name | pascal_case }}InterfaceConfig()
        >>> interface = {{ module_name | pascal_case }}Interface(config=config)
        >>> interface.connect()
        >>> interface.initialize()
        >>> result = interface.capture(exposure_ms=50.0)
        >>> status = interface.get_status()
        >>> interface.shutdown()
        >>> interface.disconnect()
    """

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

        Args:
            config: Interface configuration. Uses defaults if None.
            **kwargs: Additional configuration options.
        """
        self._config = config or {{ module_name | pascal_case }}InterfaceConfig(**kwargs)
        self._connected = False
        self._initialized = False
        self._capture_count = 0
        self._last_capture: Optional[{{ module_name | pascal_case }}CaptureResult] = None

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

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

    def connect(self) -> None:
        """Establish connection to the camera.

        This method opens the communication channel to the camera
        (e.g., USB, GigE Vision, vendor SDK connection).

        Raises:
            ConnectionError: If connection fails after retries.
        """
        # TODO: Implement actual connection logic
        # Example:
        # for attempt in range(self._config.retry_count):
        #     try:
        #         self._camera = CameraSDK.open(
        #             timeout=self._config.connection_timeout
        #         )
        #         break
        #     except ConnectionError:
        #         if attempt == self._config.retry_count - 1:
        #             raise

        logger.info("Connecting to {{ module_name }} camera...")
        self._connected = True
        logger.info("Connected to {{ module_name }} camera")

    def disconnect(self) -> None:
        """Close connection to the camera.

        This method safely closes the communication channel. It should
        be called after shutdown() to ensure the camera is in a safe state.
        """
        if self._connected:
            # TODO: Implement actual disconnection logic
            # Example:
            # self._camera.close()

            logger.info("Disconnecting from {{ module_name }} camera...")
            self._connected = False
            self._initialized = False
            logger.info("Disconnected from {{ module_name }} camera")

    def initialize(self) -> None:
        """Initialize the camera for operation.

        This method performs camera-specific initialization such as
        setting default exposure, resolution, gain, and running self-tests.
        Must be called after connect() and before capturing images.

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

        # TODO: Implement actual camera initialization
        # Example:
        # self._camera.set_exposure(self._config.exposure_ms)
        # self._camera.set_resolution(self._config.resolution)
        # self._camera.set_gain(self._config.gain)
        # self._camera.start_acquisition()

        logger.info("Initializing {{ module_name }} camera...")
        self._initialized = True
        logger.info("{{ module_name | pascal_case }} camera initialized")

    def shutdown(self) -> None:
        """Safely shut down the camera.

        This method puts the camera into a safe state before disconnecting.
        It may include stopping acquisition, saving calibration data,
        or releasing locked resources.

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

        # TODO: Implement actual shutdown logic
        # Example:
        # self._camera.stop_acquisition()
        # self._camera.save_calibration()

        logger.info("Shutting down {{ module_name }} camera...")
        self._initialized = False
        logger.info("{{ module_name | pascal_case }} camera shut down")

    def capture(
        self,
        exposure_ms: Optional[float] = None,
        save_path: Optional[str] = None,
    ) -> {{ module_name | pascal_case }}CaptureResult:
        """Capture an image from the camera.

        Args:
            exposure_ms: Exposure time in milliseconds. Uses default if None.
            save_path: Path to save the captured image. If None, image is
                not saved to disk.

        Returns:
            {{ module_name | pascal_case }}CaptureResult: Information about the captured image.

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

        effective_exposure = exposure_ms if exposure_ms is not None else self._config.exposure_ms

        # TODO: Implement actual image capture
        # Example:
        # if exposure_ms is not None:
        #     self._camera.set_exposure(exposure_ms)
        # frame = self._camera.grab_frame()
        # if save_path:
        #     frame.save(save_path)

        logger.info(
            f"Capturing image with {{ module_name }} camera (exposure={effective_exposure}ms)"
        )

        # Parse resolution
        width, height = (int(x) for x in self._config.resolution.split("x"))

        self._capture_count += 1
        result = {{ module_name | pascal_case }}CaptureResult(
            image_path=save_path,
            width=width,
            height=height,
            timestamp=time.time(),
            metadata={
                "exposure_ms": effective_exposure,
                "gain": self._config.gain,
                "resolution": self._config.resolution,
                "capture_number": self._capture_count,
            },
        )
        self._last_capture = result
        return result

    def configure(
        self,
        exposure_ms: Optional[float] = None,
        gain: Optional[float] = None,
        resolution: Optional[str] = None,
    ) -> dict:
        """Update camera settings.

        Args:
            exposure_ms: New exposure time in milliseconds.
            gain: New gain multiplier.
            resolution: New resolution string (WIDTHxHEIGHT).

        Returns:
            dict: The updated camera settings.

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

        # TODO: Implement actual camera configuration
        # Example:
        # if exposure_ms is not None:
        #     self._camera.set_exposure(exposure_ms)
        # if gain is not None:
        #     self._camera.set_gain(gain)
        # if resolution is not None:
        #     self._camera.set_resolution(resolution)

        if exposure_ms is not None:
            self._config.exposure_ms = exposure_ms
        if gain is not None:
            self._config.gain = gain
        if resolution is not None:
            self._config.resolution = resolution

        logger.info("Updated {{ module_name }} camera configuration")
        return {
            "exposure_ms": self._config.exposure_ms,
            "gain": self._config.gain,
            "resolution": self._config.resolution,
        }

    def get_image_info(self) -> dict:
        """Get information about the last captured image.

        Returns:
            dict: Information about the last capture, or empty dict if
                no images have been captured yet.

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

        if self._last_capture is None:
            return {}

        return self._last_capture.model_dump()

    def get_status(self) -> dict:
        """Get the current status of the camera.

        Returns:
            dict: Camera status information including connection state,
                initialization state, and camera-specific status data.

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

        # TODO: Implement actual status retrieval
        # Example:
        # hw_status = self._camera.get_status()
        # return {
        #     "connected": self._connected,
        #     "initialized": self._initialized,
        #     "temperature": hw_status.temperature,
        #     "frame_rate": hw_status.frame_rate,
        # }

        logger.info("Getting {{ module_name }} camera status")
        return {
            "connected": self._connected,
            "initialized": self._initialized,
            "resolution": self._config.resolution,
            "exposure_ms": self._config.exposure_ms,
            "capture_count": self._capture_count,
        }
