"""{{ module_description }}

This module implements a MADSci REST node for the {{ module_name }} camera
with image capture, camera configuration, and status reporting.

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

import argparse
import time
from typing import Any

from madsci.common.types.resource_types import Slot
from madsci.node_module.helpers import action
from madsci.node_module.rest_node_module import RestNode

from {{ module_name }}_interface import {{ module_name | pascal_case }}Interface
from {{ module_name }}_fake_interface import {{ module_name | pascal_case }}FakeInterface
from {{ module_name }}_types import {{ module_name | pascal_case }}NodeConfig


class {{ module_name | pascal_case }}Node(RestNode):
    """{{ module_description }}

    This node provides a REST API for controlling the {{ module_name }} camera
    with image capture, configuration, and status reporting.

    Node Architecture:
    - Inherits from RestNode for HTTP API exposure
    - Uses @action decorator to expose callable methods
    - Manages resources through ResourceClient integration
    - Logs all operations through EventClient

    Resource Management:
    - Creates and manages camera slot templates (Slot resources)
    - Initializes camera slot resource instances
    - Tracks resource state changes
    """

    # Hardware interface instance (initialized in startup_handler)
    _interface: Any = None

    # Configuration instance with node-specific settings
    config: {{ module_name | pascal_case }}NodeConfig = {{ module_name | pascal_case }}NodeConfig()

    # Pydantic model class for configuration validation
    config_model = {{ module_name | pascal_case }}NodeConfig

    def startup_handler(self) -> None:
        """Initialize the camera node and create resource templates.

        This method is called when the node starts up or restarts. It performs:
        1. Hardware interface initialization (real or fake based on config)
        2. Camera connection and initialization
        3. Resource template creation for camera slots
        4. Instantiation of specific resources
        5. Registration of resources with the resource manager
        """
        # Create the appropriate interface based on configuration
        if self.config.interface_type == "fake":
            self._interface = {{ module_name | pascal_case }}FakeInterface()
        else:
            self._interface = {{ module_name | pascal_case }}Interface()

        # Connect to the camera and initialize it
        self._interface.connect()
        self._interface.initialize()

        # Create camera slot template - represents positions where items can be placed
        camera_slot = Slot(
            resource_name="{{ module_name }}_slot",
            resource_class="{{ module_name | pascal_case }}Slot",
            capacity=1,
            attributes={
                "slot_type": "camera_slot",
                "accessible": True,
                "description": "{{ module_name | pascal_case }} camera slot for imaging targets",
            },
        )

        self.resource_client.init_template(
            resource=camera_slot,
            template_name="{{ module_name }}_slot",
            description=(
                "Template for {{ module_name }} camera slot. "
                "Represents positions where items can be placed for imaging."
            ),
            required_overrides=["resource_name"],
            tags=["{{ module_name }}", "slot", "camera"],
            created_by=self.node_definition.node_id,
            version="1.0.0",
        )

        # Initialize camera slot resource instance
        slot_resource_name = f"{{ module_name }}_slot_{self.node_definition.node_name}"
        self.camera_slot = self.resource_client.create_resource_from_template(
            template_name="{{ module_name }}_slot",
            resource_name=slot_resource_name,
            add_to_database=True,
        )
        self.logger.log(
            f"Initialized camera slot resource from template: {self.camera_slot.resource_id}"
        )

        self.logger.log("{{ module_name | pascal_case }} camera node initialized!")

    def shutdown_handler(self) -> None:
        """Clean shutdown of the camera node.

        This method is called when the node is being shut down or restarted.
        It safely shuts down the camera and closes connections.
        """
        self.logger.log("Shutting down {{ module_name }} camera node")
        if self._interface:
            self._interface.shutdown()
            self._interface.disconnect()

    def state_handler(self) -> dict[str, Any]:
        """Report current node state for monitoring and debugging.

        This method is called periodically by the MADSci framework to collect
        node status information for dashboard displays, health monitoring,
        and scheduling decisions.

        Returns:
            dict: Current node state including camera status information.
        """
        if self._interface and self._interface.is_connected:
            self.node_state = self._interface.get_status()

    @action
    def capture(self, exposure_ms: float = 0.0) -> dict:
        """Capture an image from the camera.

        This action triggers an image capture on the camera hardware.
        An exposure_ms of 0.0 means use the default exposure setting.

        Args:
            exposure_ms: Exposure time in milliseconds (0.0 = use default).

        Returns:
            dict: Capture result including image dimensions and metadata.
        """
        effective_exposure = exposure_ms if exposure_ms > 0.0 else None
        result = self._interface.capture(exposure_ms=effective_exposure)
        time.sleep(self.config.wait_time)
        return result.model_dump()

    @action
    def configure_camera(self, exposure_ms: float = 0.0, gain: float = 0.0) -> dict:
        """Configure camera settings.

        This action updates camera parameters. A value of 0.0 means
        the parameter is left unchanged.

        Args:
            exposure_ms: New exposure time in milliseconds (0.0 = no change).
            gain: New gain multiplier (0.0 = no change).

        Returns:
            dict: The updated camera settings.
        """
        kwargs: dict[str, Any] = {}
        if exposure_ms > 0.0:
            kwargs["exposure_ms"] = exposure_ms
        if gain > 0.0:
            kwargs["gain"] = gain
        return self._interface.configure(**kwargs)

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

        This action queries the camera for its current status information,
        including connection state, initialization state, resolution,
        exposure, and capture count.

        Returns:
            dict: Camera status information.
        """
        return self._interface.get_status()

    @action
    def reset_camera(self) -> str:
        """Reset the camera to its initial state.

        This action performs a camera reset by shutting down and
        re-initializing the camera. Useful for recovering from
        error states or preparing for a new experiment.

        Returns:
            str: Confirmation message.
        """
        self.logger.log("Resetting {{ module_name }} camera...")
        self._interface.shutdown()
        self._interface.initialize()
        self.logger.log("{{ module_name | pascal_case }} camera reset complete")
        return "Camera reset successfully"


def main() -> None:
    """Entry point for the {{ module_name }} camera node."""
    parser = argparse.ArgumentParser(description="{{ module_description }}")
    parser.add_argument(
        "--fake",
        action="store_true",
        help="Use fake interface for testing without hardware",
    )
    parser.add_argument(
        "--port",
        type=int,
        default={{ port }},
        help="Port to run the node on (default: {{ port }})",
    )
    args = parser.parse_args()

    node = {{ module_name | pascal_case }}Node()
    if args.fake:
        node.config.interface_type = "fake"
    node.config.node_url = f"http://localhost:{args.port}/"
    node.start_node()


if __name__ == "__main__":
    main()
