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

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

統帥鐵律 2026-03-23:
- 禁止假數據 (必須真實連接資源)
- 每個檢查 2 秒超時
- 失敗不導致 API 崩潰
"""

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

import httpx
from fastapi import APIRouter
from pydantic import BaseModel

from src.core.config import settings
from src.core.logging import get_logger

router = APIRouter()
logger = get_logger("awoooi.health")

# Health check timeout (seconds)
HEALTH_CHECK_TIMEOUT = 2.0


class ComponentStatus(BaseModel):
    """Individual component status"""
    name: str
    status: Literal["up", "down", "degraded"]
    latency_ms: float | None = None
    message: str | None = None


class HealthResponse(BaseModel):
    """Full health check response"""
    status: Literal["healthy", "degraded", "unhealthy"]
    version: str
    environment: str
    timestamp: datetime
    components: dict[str, Literal["up", "down", "degraded"]]


# =============================================================================
# Real Health Check Functions (統帥鐵律: 禁止假數據)
# =============================================================================


async def check_database() -> Literal["up", "down"]:
    """
    Check PostgreSQL connection using asyncpg

    統帥鐵律: 真實執行 SELECT 1，禁止假數據
    """
    try:
        import asyncpg

        # Parse DATABASE_URL for asyncpg (remove +asyncpg suffix)
        db_url = settings.DATABASE_URL.replace("postgresql+asyncpg://", "postgresql://")

        conn = await asyncio.wait_for(
            asyncpg.connect(db_url),
            timeout=HEALTH_CHECK_TIMEOUT,
        )
        try:
            result = await asyncio.wait_for(
                conn.fetchval("SELECT 1"),
                timeout=HEALTH_CHECK_TIMEOUT,
            )
            if result == 1:
                logger.debug("health_check_database", status="up")
                return "up"
            else:
                logger.warning("health_check_database", status="down", reason="unexpected_result")
                return "down"
        finally:
            await conn.close()
    except TimeoutError:
        logger.warning("health_check_database", status="down", reason="timeout")
        return "down"
    except Exception as e:
        logger.warning("health_check_database", status="down", error=str(e))
        return "down"


async def check_redis() -> Literal["up", "down"]:
    """
    Check Redis connection using redis.ping()

    統帥鐵律: 真實執行 PING，禁止假數據
    """
    try:
        import redis.asyncio as redis_lib

        # Create temporary connection for health check (avoid pool dependency)
        client = redis_lib.from_url(
            settings.REDIS_URL,
            encoding="utf-8",
            decode_responses=True,
            socket_timeout=HEALTH_CHECK_TIMEOUT,
            socket_connect_timeout=HEALTH_CHECK_TIMEOUT,
        )
        try:
            result = await asyncio.wait_for(
                client.ping(),
                timeout=HEALTH_CHECK_TIMEOUT,
            )
            if result:
                logger.debug("health_check_redis", status="up")
                return "up"
            else:
                logger.warning("health_check_redis", status="down", reason="ping_failed")
                return "down"
        finally:
            await client.close()
    except TimeoutError:
        logger.warning("health_check_redis", status="down", reason="timeout")
        return "down"
    except Exception as e:
        logger.warning("health_check_redis", status="down", error=str(e))
        return "down"


async def check_ollama() -> Literal["up", "down", "degraded"]:
    """
    Check Ollama 三層容災狀態（primary → secondary → tertiary）

    統帥鐵律: 真實 HTTP 請求，禁止假數據
    2026-05-04 ogt: 改為檢查三台（OLLAMA_URL / SECONDARY / FALLBACK），
    只要有任一台 up → "up"；primary down 但 fallback up → "degraded"；
    全部 down → "down"。反映 K8s 實際可用的 Ollama 路由狀態。
    """
    urls = [
        (settings.OLLAMA_URL, "primary"),
        (getattr(settings, "OLLAMA_SECONDARY_URL", ""), "secondary"),
        (getattr(settings, "OLLAMA_FALLBACK_URL", ""), "tertiary"),
    ]
    any_up = False
    primary_up = False

    async with httpx.AsyncClient(timeout=HEALTH_CHECK_TIMEOUT) as client:
        for i, (url, label) in enumerate(urls):
            if not url:
                continue
            try:
                response = await client.get(f"{url}/api/tags")
                if response.status_code == 200:
                    any_up = True
                    if i == 0:
                        primary_up = True
                    logger.debug("health_check_ollama", status="up", tier=label, url=url)
                    break  # 找到第一台可用就停
                else:
                    logger.debug("health_check_ollama_tier", tier=label, status_code=response.status_code)
            except (httpx.TimeoutException, httpx.ConnectError, httpx.NetworkError):
                logger.debug("health_check_ollama_tier", tier=label, status="unreachable")
            except Exception as e:
                logger.warning("health_check_ollama_tier", tier=label, error=str(e))

    if primary_up:
        return "up"
    elif any_up:
        logger.warning("health_check_ollama", status="degraded", reason="primary down, fallback active")
        return "degraded"
    else:
        logger.warning("health_check_ollama", status="down", reason="all tiers unreachable")
        return "down"


async def check_openclaw() -> Literal["up", "down"]:
    """
    Check OpenClaw service via /health endpoint

    統帥鐵律: 真實 HTTP 請求，禁止假數據
    """
    try:
        async with httpx.AsyncClient(timeout=HEALTH_CHECK_TIMEOUT) as client:
            response = await client.get(f"{settings.OPENCLAW_URL}/health")
            if response.status_code == 200:
                logger.debug("health_check_openclaw", status="up")
                return "up"
            else:
                logger.warning(
                    "health_check_openclaw",
                    status="down",
                    status_code=response.status_code,
                )
                return "down"
    except httpx.TimeoutException:
        logger.warning("health_check_openclaw", status="down", reason="timeout")
        return "down"
    except Exception as e:
        logger.warning("health_check_openclaw", status="down", error=str(e))
        return "down"


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

    Returns overall system health and individual component statuses.
    Used for monitoring dashboards and alerting.

    統帥鐵律 2026-03-23: 禁止假數據，所有檢查必須真實連接
    """
    # API is always up if this endpoint responds
    api_status: Literal["up", "down", "degraded"] = "up"

    # Run all health checks concurrently with timeout protection
    start_time = time.monotonic()

    db_task = asyncio.create_task(check_database())
    redis_task = asyncio.create_task(check_redis())
    ollama_task = asyncio.create_task(check_ollama())
    openclaw_task = asyncio.create_task(check_openclaw())

    # Wait for all tasks (each has internal timeout)
    db_status, redis_status, ollama_status, openclaw_status = await asyncio.gather(
        db_task, redis_task, ollama_task, openclaw_task,
        return_exceptions=False,
    )

    elapsed_ms = (time.monotonic() - start_time) * 1000

    components: dict[str, Literal["up", "down", "degraded"]] = {
        "api": api_status,
        "database": db_status,
        "redis": redis_status,
        "ollama": ollama_status,
        "openclaw": openclaw_status,
    }

    # Determine overall status
    down_count = sum(1 for s in components.values() if s == "down")
    degraded_count = sum(1 for s in components.values() if s == "degraded")

    if down_count > 0:
        overall_status: Literal["healthy", "degraded", "unhealthy"] = "unhealthy"
    elif degraded_count > 0:
        overall_status = "degraded"
    else:
        overall_status = "healthy"

    logger.info(
        "health_check",
        status=overall_status,
        components=components,
        elapsed_ms=round(elapsed_ms, 2),
    )

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


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

    Returns 200 when the service is ready to accept traffic.
    Used by K8s to determine if pod should receive traffic.
    """
    # TODO: Check if all required connections are established
    logger.debug("readiness_check", ready=True)
    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_check", alive=True)
    return {"status": "alive"}
