"""Interface for {{ module_name }} robot arm.

This module provides the hardware interface for communicating with
the {{ module_name }} robot arm. The interface includes full lifecycle
management: connect, initialize, shutdown, disconnect, and robot arm
operations: home, pick, place, move, and status reporting.

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

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

import logging
from typing import Any, Optional

from {{ module_name }}_types import {{ module_name | pascal_case }}InterfaceConfig

logger = logging.getLogger(__name__)


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

    This class encapsulates all hardware communication logic, including
    device lifecycle management (connect, initialize, shutdown, disconnect)
    and robot arm operations (home, pick, place, move).

    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()
        >>> interface.home()
        >>> result = interface.pick("sample_station")
        >>> result = interface.place("analysis_station")
        >>> 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._homed = False
        self._holding_item = False
        self._position: dict[str, float] = {"x": 0.0, "y": 0.0, "z": 0.0}

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

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

    @property
    def is_homed(self) -> bool:
        """Check if the robot arm has been homed."""
        return self._homed

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

        This method opens the communication channel to the robot arm
        (e.g., serial port, TCP/IP socket, 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._connection = RobotDriver.connect(
        #             timeout=self._config.connection_timeout
        #         )
        #         break
        #     except ConnectionError:
        #         if attempt == self._config.retry_count - 1:
        #             raise

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

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

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

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

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

        This method performs arm-specific initialization such as
        enabling motors, loading calibration data, and self-test.
        Must be called after connect() and before executing operations.

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

        # TODO: Implement actual robot arm initialization
        # Example:
        # self._connection.enable_motors()
        # self._connection.load_calibration()
        # self._connection.self_test()

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

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

        This method puts the arm into a safe state before disconnecting.
        It may include parking the arm, disabling motors, or releasing
        any held items.

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

        # TODO: Implement actual shutdown logic
        # Example:
        # self._connection.park()
        # self._connection.disable_motors()
        # self._connection.release_gripper()

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

    def home(self) -> None:
        """Return the robot arm to its home position.

        This method moves the arm to its calibrated home/reference position.
        It should be called after initialization and before performing
        pick/place operations.

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

        # TODO: Implement actual homing logic
        # Example:
        # self._connection.home()
        # self._connection.wait_for_home_complete()

        logger.info("Homing {{ module_name }} robot arm...")
        self._homed = True
        self._position = {"x": 0.0, "y": 0.0, "z": 0.0}
        logger.info("{{ module_name | pascal_case }} robot arm homed")

    def pick(self, location: str, **kwargs: Any) -> dict:
        """Pick up an item from a named location.

        Args:
            location: Name of the location to pick from.
            **kwargs: Additional parameters (e.g., grip_force, approach_height).

        Returns:
            dict: Result of the pick operation including success status.

        Raises:
            RuntimeError: If not connected, not initialized, or not homed.
        """
        if not self._connected:
            raise RuntimeError("Not connected to robot arm. Call connect() first.")
        if not self._initialized:
            raise RuntimeError("Robot arm not initialized. Call initialize() first.")
        if not self._homed:
            raise RuntimeError("Robot arm not homed. Call home() first.")

        # TODO: Implement actual pick logic
        # Example:
        # self._connection.move_to_location(location)
        # self._connection.close_gripper(**kwargs)
        # self._connection.retract()

        logger.info(f"Picking from location: {location}")
        self._holding_item = True
        return {
            "action": "pick",
            "location": location,
            "parameters": kwargs,
            "success": True,
        }

    def place(self, location: str, **kwargs: Any) -> dict:
        """Place an item at a named location.

        Args:
            location: Name of the location to place at.
            **kwargs: Additional parameters (e.g., release_height, settle_time).

        Returns:
            dict: Result of the place operation including success status.

        Raises:
            RuntimeError: If not connected, not initialized, or not homed.
        """
        if not self._connected:
            raise RuntimeError("Not connected to robot arm. Call connect() first.")
        if not self._initialized:
            raise RuntimeError("Robot arm not initialized. Call initialize() first.")
        if not self._homed:
            raise RuntimeError("Robot arm not homed. Call home() first.")

        # TODO: Implement actual place logic
        # Example:
        # self._connection.move_to_location(location)
        # self._connection.open_gripper(**kwargs)
        # self._connection.retract()

        logger.info(f"Placing at location: {location}")
        self._holding_item = False
        return {
            "action": "place",
            "location": location,
            "parameters": kwargs,
            "success": True,
        }

    def move(self, x: float, y: float, z: float, speed: Optional[float] = None) -> dict:
        """Move the arm to a specific position.

        Args:
            x: Target X coordinate in mm.
            y: Target Y coordinate in mm.
            z: Target Z coordinate in mm.
            speed: Movement speed in mm/s (optional, uses default if None).

        Returns:
            dict: Result of the move operation including final position.

        Raises:
            RuntimeError: If not connected, not initialized, or not homed.
        """
        if not self._connected:
            raise RuntimeError("Not connected to robot arm. Call connect() first.")
        if not self._initialized:
            raise RuntimeError("Robot arm not initialized. Call initialize() first.")
        if not self._homed:
            raise RuntimeError("Robot arm not homed. Call home() first.")

        # TODO: Implement actual move logic
        # Example:
        # self._connection.move_to(x=x, y=y, z=z, speed=speed)
        # self._connection.wait_for_move_complete()

        logger.info(f"Moving {{ module_name }} arm to ({x}, {y}, {z})")
        self._position = {"x": x, "y": y, "z": z}
        return {
            "action": "move",
            "position": {"x": x, "y": y, "z": z},
            "speed": speed,
            "success": True,
        }

    def get_position(self) -> dict:
        """Get the current position of the robot arm end-effector.

        Returns:
            dict: Current position with x, y, z coordinates.

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

        return dict(self._position)

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

        Returns:
            dict: Robot arm status information including connection state,
                initialization state, homing state, and position data.

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

        # TODO: Implement actual status retrieval
        # Example:
        # hw_status = self._connection.get_status()
        # return {
        #     "connected": self._connected,
        #     "initialized": self._initialized,
        #     "homed": self._homed,
        #     "holding_item": self._holding_item,
        #     "position": hw_status.position,
        # }

        logger.info("Getting {{ module_name }} robot arm status")
        return {
            "connected": self._connected,
            "initialized": self._initialized,
            "homed": self._homed,
            "holding_item": self._holding_item,
            "position": dict(self._position),
        }
