"""
Health Check Endpoints
======================
K8s probes + Real component health checks

Endpoints:
- GET /health         - Full health check with component probes
- GET /health/ready   - K8s readinessProbe
- GET /health/live    - K8s livenessProbe

Components Checked:
- PostgreSQL (192.168.0.188:5432)
- Redis (192.168.0.188:6380)
- Ollama ADR-110 provider pool (GCP-A -> GCP-B -> 111)
- OpenClaw (192.168.0.188:8089)
- SigNoz (192.168.0.188:3301)
"""

import asyncio
from datetime import UTC, datetime
from typing import Literal

from fastapi import APIRouter
from pydantic import BaseModel

from src.core.config import settings
from src.core.logging import get_logger
from src.services.health_check_service import get_health_check_service
from src.services.ollama_endpoint_circuit_breaker import (
    get_ollama_endpoint_cooldown_remaining_seconds,
    record_ollama_endpoint_failure,
    record_ollama_endpoint_success,
)
from src.services.ollama_endpoint_resolver import resolve_ollama_order

router = APIRouter()
logger = get_logger("awoooi.health")
CORE_COMPONENTS = ("api", "postgresql", "redis", "ollama", "openclaw", "signoz")


# =============================================================================
# Response Models
# =============================================================================

class ComponentHealth(BaseModel):
    """Individual component health status"""
    status: Literal["up", "down", "degraded"]
    latency_ms: float | None = None
    error: str | None = None
    provider_name: str | None = None
    diagnosis_code: str | None = None
    retry_after_seconds: float | None = None
    cooldown_remaining_seconds: float | None = None
    is_cooldown: bool = False


class HealthResponse(BaseModel):
    """Full health check response"""
    status: Literal["healthy", "degraded", "unhealthy"]
    version: str
    environment: str
    mock_mode: bool
    timestamp: datetime
    components: dict[str, ComponentHealth]
    ollama_route_order: list[str] = []


# =============================================================================
# Health Check Functions (Async-First)
# =============================================================================

async def _http_health_check(
    name: str,
    url: str,
    path: str = "/health",
) -> ComponentHealth:
    """
    Generic async HTTP health check

    Phase 22 P0 修復: 使用 HealthCheckService (2026-03-31)
    """
    service = get_health_check_service()
    result = await service.http_health_check(name=name, url=url, path=path)
    return ComponentHealth(
        status=result.status,
        latency_ms=result.latency_ms,
        error=result.error,
    )


async def check_postgresql() -> ComponentHealth:
    """
    Async PostgreSQL health check via TCP connect

    Phase 22 P0 修復: 使用 HealthCheckService (2026-03-31)
    """
    service = get_health_check_service()
    result = await service.postgresql_tcp_check()
    return ComponentHealth(
        status=result.status,
        latency_ms=result.latency_ms,
        error=result.error,
    )


async def check_redis() -> ComponentHealth:
    """
    Async Redis health check via PING

    Phase 22 P0 修復: 使用 HealthCheckService (2026-03-31)
    """
    service = get_health_check_service()
    result = await service.redis_ping_check()
    return ComponentHealth(
        status=result.status,
        latency_ms=result.latency_ms,
        error=result.error,
    )


async def check_ollama() -> ComponentHealth:
    """Async aggregate Ollama health check via ADR-110 provider chain."""
    aggregate, _details = await check_ollama_provider_chain()
    return aggregate


async def check_ollama_provider_chain() -> tuple[ComponentHealth, dict[str, ComponentHealth]]:
    """
    Check the full Ollama provider chain.

    The aggregate ``ollama`` component represents route availability:
    - up: GCP-A is reachable
    - degraded: GCP-A is unavailable but GCP-B or 111 is reachable
    - down: no configured Ollama endpoint is reachable
    """
    selections = tuple(
        selection
        for selection in resolve_ollama_order("healthcheck")
        if selection.url and selection.provider_name != "ollama_unconfigured"
    )
    if not selections:
        aggregate = ComponentHealth(
            status="down",
            error="no Ollama endpoints configured",
        )
        return aggregate, {}

    checked = await asyncio.gather(
        *(
            _ollama_endpoint_health_check(selection.provider_name, selection.url)
            for selection in selections
        )
    )
    details = {
        selection.provider_name: result
        for selection, result in zip(selections, checked, strict=False)
    }

    primary = selections[0]
    primary_status = details[primary.provider_name].status
    if primary.provider_name == "ollama_gcp_a" and primary_status == "up":
        return details[primary.provider_name], details

    first_available = next(
        (
            selection
            for selection in selections
            if details[selection.provider_name].status == "up"
        ),
        None,
    )
    if first_available:
        fallback = details[first_available.provider_name]
        return (
            ComponentHealth(
                status="degraded",
                latency_ms=fallback.latency_ms,
                error=f"primary unavailable; fallback active: {first_available.provider_name}",
            ),
            details,
        )

    errors = ", ".join(
        f"{provider}={health.error or health.status}"
        for provider, health in details.items()
    )
    return (
        ComponentHealth(
            status="down",
            error=f"all Ollama endpoints unavailable: {errors}",
        ),
        details,
    )


async def _ollama_endpoint_health_check(name: str, url: str) -> ComponentHealth:
    cooldown_remaining = get_ollama_endpoint_cooldown_remaining_seconds(url)
    if cooldown_remaining > 0:
        return ComponentHealth(
            status="down",
            error=f"recent endpoint failure cooldown: {cooldown_remaining:.0f}s",
            provider_name=name,
            diagnosis_code="endpoint_cooldown",
            retry_after_seconds=round(cooldown_remaining, 1),
            cooldown_remaining_seconds=round(cooldown_remaining, 1),
            is_cooldown=True,
        )

    result = await _http_health_check(name, url, "/api/tags")
    result.provider_name = name
    if result.status == "up":
        result.diagnosis_code = "endpoint_reachable"
        record_ollama_endpoint_success(url)
    else:
        result.diagnosis_code = _classify_ollama_endpoint_failure(name, result.error)
        record_ollama_endpoint_failure(url)
    return result


def _classify_ollama_endpoint_failure(
    provider_name: str,
    error: str | None,
) -> str:
    """Return a stable diagnosis code for UI/alert rendering."""
    normalized_error = (error or "").lower()
    if "cooldown" in normalized_error:
        return "endpoint_cooldown"
    if "502" in normalized_error or "bad gateway" in normalized_error:
        return (
            "local_proxy_upstream_unreachable"
            if provider_name == "ollama_local"
            else "proxy_upstream_unreachable"
        )
    if "timeout" in normalized_error:
        return "endpoint_timeout"
    if "connection refused" in normalized_error:
        return "endpoint_connection_refused"
    if "no route to host" in normalized_error or "network is unreachable" in normalized_error:
        return "endpoint_network_unreachable"
    return "endpoint_unreachable"


async def check_openclaw() -> ComponentHealth:
    """Async OpenClaw health check via /health"""
    return await _http_health_check("openclaw", settings.OPENCLAW_URL, "/health")


async def check_signoz() -> ComponentHealth:
    """Async SigNoz health check"""
    return await _http_health_check("signoz", settings.SIGNOZ_URL, "/api/v1/health")


def _determine_overall_status(
    components: dict[str, ComponentHealth],
) -> Literal["healthy", "degraded", "unhealthy"]:
    """Determine overall health from core aggregate components only."""
    statuses = [
        components[name].status
        for name in CORE_COMPONENTS
        if name in components
    ]
    down_count = statuses.count("down")
    degraded_count = statuses.count("degraded")

    critical_down = (
        components.get("postgresql", ComponentHealth(status="down")).status == "down"
        or components.get("redis", ComponentHealth(status="down")).status == "down"
    )

    if critical_down or down_count >= 3:
        return "unhealthy"
    if down_count >= 1 or degraded_count > 0:
        return "degraded"
    return "healthy"


# =============================================================================
# Endpoints
# =============================================================================

@router.get("/health", response_model=HealthResponse)
async def get_health() -> HealthResponse:
    """
    Full health check with real component probes

    Performs async health checks on all external dependencies:
    - PostgreSQL: Primary database
    - Redis: Cache layer
    - Ollama: Local LLM service
    - OpenClaw: AI Agent service
    - SigNoz: Observability platform

    Returns overall system status based on component health.
    """
    # Run all health checks concurrently (Async-First)
    results = await asyncio.gather(
        check_postgresql(),
        check_redis(),
        check_ollama_provider_chain(),
        check_openclaw(),
        check_signoz(),
    )

    ollama_aggregate, ollama_details = results[2]
    components = {
        "api": ComponentHealth(status="up", latency_ms=0.0),
        "postgresql": results[0],
        "redis": results[1],
        "ollama": ollama_aggregate,
        "openclaw": results[3],
        "signoz": results[4],
    }
    components.update(ollama_details)

    overall_status = _determine_overall_status(components)
    ollama_route_order = [
        selection.provider_name
        for selection in resolve_ollama_order("healthcheck")
        if selection.url and selection.provider_name != "ollama_unconfigured"
    ]

    logger.info(
        "health_check_complete",
        status=overall_status,
        mock_mode=settings.MOCK_MODE,
        components={k: v.status for k, v in components.items()},
    )

    return HealthResponse(
        status=overall_status,
        version=settings.VERSION,
        environment=settings.ENVIRONMENT,
        mock_mode=settings.MOCK_MODE,
        timestamp=datetime.now(UTC),
        components=components,
        ollama_route_order=ollama_route_order,
    )


@router.get("/health/ready")
async def get_readiness() -> dict[str, str]:
    """
    K8s readinessProbe

    Returns 200 when the service is ready to accept traffic.
    Lightweight check - doesn't probe external services.
    """
    logger.debug("readiness_probe")
    return {"status": "ready"}


@router.get("/health/live")
async def get_liveness() -> dict[str, str]:
    """
    K8s livenessProbe

    Returns 200 when the service is alive.
    Used by K8s to determine if pod needs restart.
    """
    logger.debug("liveness_probe")
    return {"status": "alive"}


@router.get("/health/ai-usage")
async def get_ai_usage() -> dict:
    """
    AI API 用量監控

    Phase 13.2: Rate Limiter 整合 (2026-03-26)
    監控 Gemini/Claude API 使用量，防止暴衝。

    Returns:
        dict: 各 provider 用量統計
    """
    from src.services.ai_rate_limiter import get_ai_rate_limiter

    rate_limiter = get_ai_rate_limiter()

    gemini_stats = await rate_limiter.get_usage_stats("gemini")
    claude_stats = await rate_limiter.get_usage_stats("claude")

    return {
        "gemini": gemini_stats,
        "claude": claude_stats,
        "fallback_order": settings.AI_FALLBACK_ORDER,
    }
