"""Health check helpers for service monitoring."""

import asyncio
from collections.abc import Callable, Coroutine
from datetime import datetime
from typing import TYPE_CHECKING, Any

from .exceptions import HealthCheckError
from .models import HealthStatus

if TYPE_CHECKING:
    from .client import VikdataClient


class HealthChecker:
    """Health checker for monitoring service components.

    Allows registering multiple health checks that run periodically
    and report status back to vikdata.
    """

    def __init__(self) -> None:
        """Initialize health checker."""
        self._checks: dict[str, Callable[[], Coroutine[Any, Any, bool]]] = {}
        self._results: dict[str, bool] = {}
        self._details: dict[str, Any] = {}
        self._last_check: datetime | None = None

    def add_check(
        self,
        name: str,
        check_fn: Callable[[], Coroutine[Any, Any, bool]],
    ) -> None:
        """Add a health check.

        Args:
            name: Name of the check (e.g., "database", "redis")
            check_fn: Async function that returns True if healthy
        """
        self._checks[name] = check_fn

    def remove_check(self, name: str) -> None:
        """Remove a health check.

        Args:
            name: Name of the check to remove
        """
        self._checks.pop(name, None)
        self._results.pop(name, None)
        self._details.pop(name, None)

    async def check(self) -> dict[str, Any]:
        """Run all health checks.

        Returns:
            Health check results with overall status

        Raises:
            HealthCheckError: If health checks fail catastrophically
        """
        self._results.clear()
        self._details.clear()
        self._last_check = datetime.utcnow()

        # Run all checks in parallel
        try:
            results = await asyncio.gather(
                *[self._run_check(name, check_fn) for name, check_fn in self._checks.items()],
                return_exceptions=True,
            )

            # Process results
            for i, (name, _) in enumerate(self._checks.items()):
                result = results[i]
                if isinstance(result, Exception):
                    self._results[name] = False
                    self._details[name] = {"error": str(result)}
                else:
                    self._results[name] = result
                    self._details[name] = {"healthy": result}

        except Exception as e:
            raise HealthCheckError(f"Health check execution failed: {e}") from e

        # Determine overall status
        if not self._results:
            status = "healthy"  # No checks means healthy by default
        elif all(self._results.values()):
            status = "healthy"
        elif any(self._results.values()):
            status = "degraded"
        else:
            status = "unhealthy"

        return {
            "status": status,
            "timestamp": self._last_check.isoformat(),
            "checks": self._details,
        }

    async def _run_check(
        self,
        name: str,
        check_fn: Callable[[], Coroutine[Any, Any, bool]],
    ) -> bool:
        """Run a single health check with timeout.

        Args:
            name: Check name
            check_fn: Check function

        Returns:
            True if healthy, False otherwise
        """
        try:
            # Timeout after 5 seconds
            result = await asyncio.wait_for(check_fn(), timeout=5.0)
            return bool(result)
        except asyncio.TimeoutError:
            return False
        except Exception:
            return False

    def get_status(self) -> str:
        """Get current overall health status.

        Returns:
            "healthy", "degraded", or "unhealthy"
        """
        if not self._results:
            return "healthy"
        elif all(self._results.values()):
            return "healthy"
        elif any(self._results.values()):
            return "degraded"
        else:
            return "unhealthy"

    def get_checks(self) -> dict[str, Any]:
        """Get detailed check results.

        Returns:
            Dictionary of check results
        """
        return dict(self._details)

    def get_last_check_time(self) -> datetime | None:
        """Get timestamp of last health check.

        Returns:
            Datetime of last check, or None if never checked
        """
        return self._last_check


class HealthReporter:
    """Automatic health reporter that periodically checks and reports to vikdata."""

    def __init__(
        self,
        health_checker: HealthChecker,
        service_id: str,
        client: Any,  # VikdataClient
        interval: float = 30.0,
    ) -> None:
        """Initialize health reporter.

        Args:
            health_checker: HealthChecker instance
            service_id: Service ID registered with vikdata
            client: VikdataClient instance
            interval: Report interval in seconds
        """
        self.health_checker = health_checker
        self.service_id = service_id
        self.client = client
        self.interval = interval
        self._task: asyncio.Task[None] | None = None
        self._running = False

    async def start(self) -> None:
        """Start background health reporting."""
        if self._running:
            return

        self._running = True
        self._task = asyncio.create_task(self._report_loop())

    async def stop(self) -> None:
        """Stop background health reporting."""
        self._running = False
        if self._task:
            self._task.cancel()
            try:
                await self._task
            except asyncio.CancelledError:
                pass

    async def report_now(self) -> None:
        """Report health status immediately."""
        result = await self.health_checker.check()
        await self.client.services.report_health(
            service_id=self.service_id,
            status=result["status"],
            checks=result["checks"],
        )

    async def _report_loop(self) -> None:
        """Background loop for periodic health reporting."""
        while self._running:
            try:
                await self.report_now()
            except Exception:
                # Silently ignore reporting failures - service continues
                pass

            await asyncio.sleep(self.interval)


async def check_vikdata_health(client: "VikdataClient") -> HealthStatus:
    """Check overall vikdata platform health status.

    This function queries the vikdata health endpoint to get the current
    status of all registered services and the platform itself.

    Args:
        client: VikdataClient instance

    Returns:
        HealthStatus with overall platform health

    Raises:
        HealthCheckError: If health check fails

    Example:
        ```python
        client = VikdataClient(base_url="https://api.vikdata.com")
        status = await check_vikdata_health(client)
        if status.status != "healthy":
            print(f"Platform is {status.status}")
            for service, svc_status in status.services.items():
                print(f"  {service}: {svc_status}")
        ```
    """
    try:
        response = await client.request("GET", "/api/v1/health")
        data = response.json()
        return HealthStatus(**data)
    except Exception as e:
        raise HealthCheckError(f"Failed to check vikdata health: {e}") from e


async def wait_for_vikdata(
    client: "VikdataClient",
    timeout: float = 30.0,
    check_interval: float = 1.0,
) -> bool:
    """Wait for vikdata platform to become healthy.

    This function is useful during service startup to ensure the vikdata
    platform is available before proceeding with registration and operation.

    Args:
        client: VikdataClient instance
        timeout: Maximum time to wait in seconds (default: 30.0)
        check_interval: Time between health checks in seconds (default: 1.0)

    Returns:
        True if vikdata becomes healthy within timeout, False otherwise

    Example:
        ```python
        client = VikdataClient(base_url="https://api.vikdata.com")

        # Wait for vikdata to be ready
        if await wait_for_vikdata(client, timeout=60.0):
            # Register service
            await client.services.register(
                name="fishinfo",
                service_url="https://fishinfo.example.com",
            )
        else:
            print("Timeout waiting for vikdata")
        ```
    """
    start_time = asyncio.get_event_loop().time()

    while True:
        try:
            status = await check_vikdata_health(client)
            if status.status == "healthy":
                return True
        except (HealthCheckError, Exception):
            # Health check failed, continue waiting
            pass

        # Check timeout
        elapsed = asyncio.get_event_loop().time() - start_time
        if elapsed >= timeout:
            return False

        # Wait before next check
        await asyncio.sleep(check_interval)
