"""
Service call and device operation tools for Home Assistant MCP server.

This module provides service execution and WebSocket-enabled operation monitoring tools.
"""

from typing import Any, cast

from ..errors import (
    create_validation_error,
)
from .helpers import exception_to_structured_error
from .util_helpers import coerce_bool_param, parse_json_param


def register_service_tools(mcp, client, **kwargs):
    """Register service call and operation monitoring tools with the MCP server."""
    device_tools = kwargs.get("device_tools")
    if not device_tools:
        raise ValueError("device_tools is required for service tools registration")

    @mcp.tool(annotations={"destructiveHint": True, "title": "Call Service"})
    async def ha_call_service(
        domain: str,
        service: str,
        entity_id: str | None = None,
        data: str | dict[str, Any] | None = None,
        return_response: bool | str = False,
    ) -> dict[str, Any]:
        """
        Execute Home Assistant services to control entities and trigger automations.

        This is the universal tool for controlling all Home Assistant entities. Services follow
        the pattern domain.service (e.g., light.turn_on, climate.set_temperature).

        **Basic Usage:**
        ```python
        # Turn on a light
        ha_call_service("light", "turn_on", entity_id="light.living_room")

        # Set temperature with parameters
        ha_call_service("climate", "set_temperature",
                      entity_id="climate.thermostat", data={"temperature": 22})

        # Trigger automation
        ha_call_service("automation", "trigger", entity_id="automation.morning_routine")

        # Universal controls work with any entity
        ha_call_service("homeassistant", "toggle", entity_id="switch.porch_light")
        ```

        **Parameters:**
        - **domain**: Service domain (light, climate, automation, etc.)
        - **service**: Service name (turn_on, set_temperature, trigger, etc.)
        - **entity_id**: Optional target entity. For some services (e.g., light.turn_off), omitting this targets all entities in the domain
        - **data**: Optional dict of service-specific parameters
        - **return_response**: Set to True for services that return data

        **For detailed service documentation and parameters, use ha_get_domain_docs(domain).**

        Common patterns: Use ha_get_state() to check current values before making changes.
        Use ha_search_entities() to find correct entity IDs.
        """
        try:
            # Parse JSON data if provided as string
            try:
                parsed_data = parse_json_param(data, "data")
            except ValueError as e:
                return create_validation_error(
                    f"Invalid data parameter: {e}",
                    parameter="data",
                    invalid_json=True,
                )

            # Ensure service_data is a dict
            service_data: dict[str, Any] = {}
            if parsed_data is not None:
                if isinstance(parsed_data, dict):
                    service_data = parsed_data
                else:
                    return create_validation_error(
                        "Data parameter must be a JSON object",
                        parameter="data",
                        details=f"Received type: {type(parsed_data).__name__}",
                    )

            if entity_id:
                service_data["entity_id"] = entity_id

            # Coerce return_response boolean parameter
            return_response_bool = coerce_bool_param(return_response, "return_response", default=False) or False

            result = await client.call_service(domain, service, service_data, return_response=return_response_bool)

            response = {
                "success": True,
                "domain": domain,
                "service": service,
                "entity_id": entity_id,
                "parameters": data,
                "result": result,
                "message": f"Successfully executed {domain}.{service}",
            }

            # If return_response was requested, include the service_response key prominently
            if return_response_bool and isinstance(result, dict):
                response["service_response"] = result.get("service_response", result)

            return response
        except Exception as error:
            # Use structured error response
            error_response = exception_to_structured_error(
                error,
                context={
                    "domain": domain,
                    "service": service,
                    "entity_id": entity_id,
                },
            )
            # Add service-specific suggestions
            suggestions = [
                f"Verify {entity_id} exists using ha_get_state()" if entity_id else "Specify an entity_id for targeted service calls",
                f"Check available services for {domain} domain using ha_get_domain_docs()",
                "Use ha_search_entities() to find correct entity IDs",
            ]
            if entity_id:
                suggestions.extend([
                    f"For automation: ha_call_service('automation', 'trigger', entity_id='{entity_id}')",
                    f"For universal control: ha_call_service('homeassistant', 'toggle', entity_id='{entity_id}')",
                ])
            # Merge suggestions into error response
            if "error" in error_response and isinstance(error_response["error"], dict):
                error_response["error"]["suggestions"] = suggestions
            return error_response

    @mcp.tool(annotations={"readOnlyHint": True, "title": "Get Operation Status"})
    async def ha_get_operation_status(
        operation_id: str, timeout_seconds: int = 10
    ) -> dict[str, Any]:
        """Check status of device operation with real-time WebSocket verification."""
        result = await device_tools.get_device_operation_status(
            operation_id=operation_id, timeout_seconds=timeout_seconds
        )
        return cast(dict[str, Any], result)

    @mcp.tool(annotations={"destructiveHint": True, "title": "Bulk Control"})
    async def ha_bulk_control(
        operations: str | list[dict[str, Any]], parallel: bool | str = True
    ) -> dict[str, Any]:
        """Control multiple devices with bulk operation support and WebSocket tracking."""
        # Coerce boolean parameter that may come as string from XML-style calls
        parallel_bool = coerce_bool_param(parallel, "parallel", default=True) or True

        # Parse JSON operations if provided as string
        try:
            parsed_operations = parse_json_param(operations, "operations")
        except ValueError as e:
            return create_validation_error(
                f"Invalid operations parameter: {e}",
                parameter="operations",
                invalid_json=True,
            )

        # Ensure operations is a list of dicts
        if parsed_operations is None or not isinstance(parsed_operations, list):
            return create_validation_error(
                "Operations parameter must be a list",
                parameter="operations",
                details=f"Received type: {type(parsed_operations).__name__}",
            )

        operations_list = cast(list[dict[str, Any]], parsed_operations)
        result = await device_tools.bulk_device_control(
            operations=operations_list, parallel=parallel_bool
        )
        return cast(dict[str, Any], result)

    @mcp.tool(annotations={"readOnlyHint": True, "title": "Get Bulk Operation Status"})
    async def ha_get_bulk_status(operation_ids: list[str]) -> dict[str, Any]:
        """
        Check status of multiple device control operations.

        Use this tool to check the status of operations initiated by ha_bulk_control
        or control_device_smart. Each of these tools returns unique operation_ids
        that can be tracked here.

        **IMPORTANT:** This tool is for tracking async device operations, NOT for
        checking current entity states. To get current states of entities, use
        ha_get_state instead.

        **Args:**
            operation_ids: List of operation IDs returned by ha_bulk_control or
                          control_device_smart (e.g., ["op_1234", "op_5678"])

        **Returns:**
            Status summary with completion/pending/failed counts and detailed
            results for each operation.

        **Example:**
            # After calling control_device_smart
            result = control_device_smart("light.kitchen", "on")
            op_id = result["operation_id"]  # e.g., "op_1234"

            # Check operation status
            status = ha_get_bulk_status([op_id])
        """
        result = await device_tools.get_bulk_operation_status(
            operation_ids=operation_ids
        )
        return cast(dict[str, Any], result)
