"""{{ module_description }}

This module implements a MADSci REST node for the {{ module_name }} liquid handler
with liquid handling actions (aspirate, dispense, transfer, tips) and
resource management using templates and slots.

Generated by: madsci new module --template module/liquid_handler
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 }} liquid handler
    with liquid handling operations and resource tracking.

    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 liquid handler slot templates (Slot resources)
    - Initializes device 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 liquid handler 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. Device connection and initialization
        3. Resource template creation for liquid handler 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 device and initialize it
        self._interface.connect()
        self._interface.initialize()

        # Create liquid handler slot template - represents positions for labware
        liquid_handler_slot = Slot(
            resource_name="{{ module_name }}_slot",
            resource_class="{{ module_name | pascal_case }}Slot",
            capacity=1,
            attributes={
                "slot_type": "liquid_handler_slot",
                "accessible": True,
                "description": "{{ module_name | pascal_case }} liquid handler slot for placing labware",
            },
        )

        self.resource_client.init_template(
            resource=liquid_handler_slot,
            template_name="{{ module_name }}_slot",
            description=(
                "Template for {{ module_name }} liquid handler slot. "
                "Represents positions where labware can be placed on the deck."
            ),
            required_overrides=["resource_name"],
            tags=["{{ module_name }}", "slot", "liquid_handler"],
            created_by=self.node_definition.node_id,
            version="1.0.0",
        )

        # Initialize liquid handler slot resource instance
        slot_resource_name = f"{{ module_name }}_slot_{self.node_definition.node_name}"
        self.device_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 liquid handler slot resource from template: {self.device_slot.resource_id}"
        )

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

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

        This method is called when the node is being shut down or restarted.
        It safely shuts down the device and closes connections.
        """
        self.logger.log("Shutting down {{ module_name }} liquid handler 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 liquid handler status information.
        """
        if self._interface and self._interface.is_connected:
            self.node_state = self._interface.get_status()

    @action
    def aspirate(self, volume_ul: float, well: str = "") -> dict:
        """Aspirate liquid from a well or current position.

        This action commands the liquid handler to aspirate the specified
        volume from the given well position.

        Args:
            volume_ul: Volume to aspirate in microliters.
            well: Well identifier (e.g., 'A1'). Empty string for current position.

        Returns:
            dict: Result containing volume and well information.
        """
        self._interface.aspirate(
            volume_ul, well=well if well else None
        )
        time.sleep(self.config.wait_time)
        return {"volume_ul": volume_ul, "well": well, "action": "aspirate"}

    @action
    def dispense(self, volume_ul: float, well: str = "") -> dict:
        """Dispense liquid into a well or current position.

        This action commands the liquid handler to dispense the specified
        volume into the given well position.

        Args:
            volume_ul: Volume to dispense in microliters.
            well: Well identifier (e.g., 'A1'). Empty string for current position.

        Returns:
            dict: Result containing volume and well information.
        """
        self._interface.dispense(
            volume_ul, well=well if well else None
        )
        time.sleep(self.config.wait_time)
        return {"volume_ul": volume_ul, "well": well, "action": "dispense"}

    @action
    def transfer(
        self, volume_ul: float, source_well: str = "", dest_well: str = ""
    ) -> dict:
        """Transfer liquid from one well to another.

        This action commands the liquid handler to aspirate from the source
        well and dispense into the destination well.

        Args:
            volume_ul: Volume to transfer in microliters.
            source_well: Source well identifier (e.g., 'A1').
            dest_well: Destination well identifier (e.g., 'B1').

        Returns:
            dict: Result containing transfer details.
        """
        self._interface.transfer(
            volume_ul,
            source_well=source_well,
            dest_well=dest_well,
        )
        time.sleep(self.config.wait_time)
        return {
            "volume_ul": volume_ul,
            "source_well": source_well,
            "dest_well": dest_well,
            "action": "transfer",
        }

    @action
    def pick_up_tips(self) -> str:
        """Pick up disposable tips.

        This action commands the liquid handler to pick up tips from
        the default tip rack.

        Returns:
            str: Confirmation message.
        """
        self._interface.pick_up_tips()
        time.sleep(self.config.wait_time)
        return "Tips picked up successfully"

    @action
    def drop_tips(self) -> str:
        """Drop current tips into the waste.

        This action commands the liquid handler to drop the currently
        attached tips.

        Returns:
            str: Confirmation message.
        """
        self._interface.drop_tips()
        time.sleep(self.config.wait_time)
        return "Tips dropped successfully"

    @action
    def get_handler_status(self) -> dict:
        """Get the current status of the liquid handler.

        This action queries the liquid handler for its current status
        information, including connection state, initialization state,
        tip state, and volume data.

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


def main() -> None:
    """Entry point for the {{ module_name }} liquid handler 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()
