"""Utility protocols for SDK-provided functionality.

These protocols are automatically available to all modules and don't need to be
explicitly included in module output unions.
"""

from datetime import datetime, timezone
from typing import Any, ClassVar, Literal

from pydantic import BaseModel, Field

from digitalkin.models.module.base_types import DataTrigger


class UtilityProtocol(DataTrigger):
    """Base class for SDK-provided utility protocols.

    All SDK utility protocols inherit from this class to enable:
    - Easy identification of SDK vs user-defined protocols
    - Auto-injection capability
    - Consistent behavior across the SDK
    """


class EndOfStreamOutput(UtilityProtocol):
    """Signal that the stream has ended."""

    protocol: Literal["end_of_stream"] = "end_of_stream"  # type: ignore[misc]


class ModuleStartInfoOutput(UtilityProtocol):
    """Output sent when module starts with execution context.

    This protocol is sent as the first message when a module starts,
    providing the client with essential execution context information.
    """

    protocol: Literal["module_start_info"] = "module_start_info"  # type: ignore[misc]
    job_id: str = Field(..., description="Unique job identifier")
    mission_id: str = Field(..., description="Mission identifier")
    setup_id: str = Field(..., description="Setup identifier")
    setup_version_id: str = Field(..., description="Setup version identifier")
    module_id: str = Field(..., description="Module identifier")
    module_name: str = Field(..., description="Human-readable module name")
    started_at: str = Field(
        default_factory=lambda: datetime.now(tz=timezone.utc).isoformat(),
        description="ISO timestamp when module started",
    )


class HealthcheckPingInput(UtilityProtocol):
    """Input for healthcheck ping request."""

    protocol: Literal["healthcheck_ping"] = "healthcheck_ping"  # type: ignore[misc]


class HealthcheckPingOutput(UtilityProtocol):
    """Output for healthcheck ping response.

    Simple alive check that returns "pong" status.
    """

    protocol: Literal["healthcheck_ping"] = "healthcheck_ping"  # type: ignore[misc]
    status: Literal["pong"] = "pong"
    latency_ms: float | None = Field(
        default=None,
        description="Round-trip latency in milliseconds",
    )


class ServiceHealthStatus(BaseModel):
    """Health status of a single service."""

    name: str = Field(..., description="Name of the service")
    status: Literal["healthy", "unhealthy", "unknown"] = Field(
        ...,
        description="Health status of the service",
    )
    message: str | None = Field(
        default=None,
        description="Optional message about the service status",
    )


class HealthcheckServicesInput(UtilityProtocol):
    """Input for healthcheck services request."""

    protocol: Literal["healthcheck_services"] = "healthcheck_services"  # type: ignore[misc]


class HealthcheckServicesOutput(UtilityProtocol):
    """Output for healthcheck services response.

    Reports the health status of all configured services.
    """

    protocol: Literal["healthcheck_services"] = "healthcheck_services"  # type: ignore[misc]
    services: list[ServiceHealthStatus] = Field(
        ...,
        description="List of service health statuses",
    )
    overall_status: Literal["healthy", "degraded", "unhealthy"] = Field(
        ...,
        description="Overall health status based on all services",
    )


class HealthcheckStatusInput(UtilityProtocol):
    """Input for healthcheck status request."""

    protocol: Literal["healthcheck_status"] = "healthcheck_status"  # type: ignore[misc]


class HealthcheckStatusOutput(UtilityProtocol):
    """Output for healthcheck status response.

    Comprehensive module status including uptime, active jobs, and metadata.
    """

    protocol: Literal["healthcheck_status"] = "healthcheck_status"  # type: ignore[misc]
    module_name: str = Field(..., description="Name of the module")
    module_status: str = Field(..., description="Current status of the module")
    uptime_seconds: float | None = Field(
        default=None,
        description="Module uptime in seconds",
    )
    active_jobs: int = Field(
        default=0,
        description="Number of currently active jobs",
    )
    metadata: dict[str, Any] = Field(
        default_factory=dict,
        description="Additional metadata about the module",
    )


class UtilityRegistry:
    """Registry for SDK-provided built-in triggers.

    Example:
        builtin_triggers = UtilityRegistry.get_builtin_triggers()
    """

    _builtin_triggers: ClassVar[tuple | None] = None

    @classmethod
    def get_builtin_triggers(cls) -> tuple:
        """Get all SDK-provided built-in trigger handlers.

        Uses lazy loading to avoid circular imports with the modules package.

        Returns:
            Tuple of TriggerHandler subclasses for built-in functionality.
        """
        if cls._builtin_triggers is None:
            from digitalkin.modules.triggers.healthcheck_ping_trigger import (
                HealthcheckPingTrigger,
            )  # Lazy import to avoid circular dependency
            from digitalkin.modules.triggers.healthcheck_services_trigger import (
                HealthcheckServicesTrigger,
            )  # Lazy import to avoid circular dependency
            from digitalkin.modules.triggers.healthcheck_status_trigger import (
                HealthcheckStatusTrigger,
            )  # Lazy import to avoid circular dependency

            cls._builtin_triggers = (
                HealthcheckPingTrigger,
                HealthcheckServicesTrigger,
                HealthcheckStatusTrigger,
            )
        return cls._builtin_triggers
