"""
Health Check Service - 統一健康檢查
===================================
Phase 22 P0 修復: Router 層禁止直接 httpx.AsyncClient

遵循規範:
- Skill 09: Router 層禁止直接外部 API 呼叫
- feedback_lewooogo_modular_enforcement.md: Service 層封裝

功能:
- HTTP 健康檢查 (通用)
- PostgreSQL TCP 檢查
- Redis PING 檢查

版本: v1.0
建立: 2026-03-31 (台北時區)
建立者: Claude Code (首席架構師 P0 修復)
"""

import asyncio
from dataclasses import dataclass

import httpx
import structlog

from src.core.config import settings

logger = structlog.get_logger(__name__)


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


@dataclass
class ComponentHealth:
    """組件健康狀態"""

    status: str  # "up" | "down"
    latency_ms: float | None = None
    error: str | None = None


# =============================================================================
# Health Check Service
# =============================================================================


class HealthCheckService:
    """
    Health Check Service

    統一健康檢查，符合 leWOOOgo 積木化原則

    2026-03-31 Claude Code (Phase 22 P0 修復)
    """

    def __init__(
        self,
        default_timeout: float | None = None,
    ):
        self._default_timeout = default_timeout or settings.HEALTH_CHECK_TIMEOUT

    async def http_health_check(
        self,
        name: str,
        url: str,
        path: str = "/health",
        timeout: float | None = None,
    ) -> ComponentHealth:
        """
        通用 HTTP 健康檢查

        Args:
            name: 組件名稱 (用於日誌)
            url: 基礎 URL
            path: 健康檢查路徑
            timeout: 超時秒數

        Returns:
            ComponentHealth
        """
        if settings.MOCK_MODE:
            # Mock 模式: 模擬延遲
            import random

            latency = random.uniform(1.0, 15.0)
            return ComponentHealth(status="up", latency_ms=round(latency, 2))

        try:
            start = asyncio.get_event_loop().time()
            async with httpx.AsyncClient(
                timeout=timeout or self._default_timeout
            ) as client:
                response = await client.get(f"{url}{path}")
                response.raise_for_status()
            latency = (asyncio.get_event_loop().time() - start) * 1000
            return ComponentHealth(status="up", latency_ms=round(latency, 2))

        except httpx.TimeoutException:
            logger.warning(f"{name}_health_check_timeout", url=url)
            return ComponentHealth(status="down", error="timeout")
        except httpx.ConnectError:
            logger.warning(f"{name}_health_check_connect_error", url=url)
            return ComponentHealth(status="down", error="connection refused")
        except Exception as e:
            logger.warning(f"{name}_health_check_failed", url=url, error=str(e))
            return ComponentHealth(status="down", error=str(e))

    async def postgresql_tcp_check(
        self,
        host: str = "192.168.0.188",
        port: int = 5432,
        timeout: float | None = None,
    ) -> ComponentHealth:
        """
        PostgreSQL TCP 連線檢查

        Args:
            host: PostgreSQL 主機
            port: PostgreSQL 端口
            timeout: 超時秒數

        Returns:
            ComponentHealth
        """
        if settings.MOCK_MODE:
            import random

            return ComponentHealth(
                status="up", latency_ms=round(random.uniform(0.5, 3.0), 2)
            )

        try:
            start = asyncio.get_event_loop().time()
            reader, writer = await asyncio.wait_for(
                asyncio.open_connection(host, port),
                timeout=timeout or self._default_timeout,
            )
            writer.close()
            await writer.wait_closed()
            latency = (asyncio.get_event_loop().time() - start) * 1000
            return ComponentHealth(status="up", latency_ms=round(latency, 2))

        except TimeoutError:
            logger.warning("postgresql_health_check_timeout", host=host, port=port)
            return ComponentHealth(status="down", error="timeout")
        except ConnectionRefusedError:
            logger.warning(
                "postgresql_health_check_refused",
                host=host,
                port=port,
            )
            return ComponentHealth(status="down", error="connection refused")
        except Exception as e:
            logger.warning(
                "postgresql_health_check_failed",
                host=host,
                port=port,
                error=str(e),
            )
            return ComponentHealth(status="down", error=str(e))

    async def redis_ping_check(
        self,
        timeout: float | None = None,
    ) -> ComponentHealth:
        """
        Redis PING 檢查

        Args:
            timeout: 超時秒數

        Returns:
            ComponentHealth
        """
        if settings.MOCK_MODE:
            import random

            return ComponentHealth(
                status="up", latency_ms=round(random.uniform(0.1, 1.0), 2)
            )

        try:
            from src.core.redis_client import get_redis

            start = asyncio.get_event_loop().time()
            redis = get_redis()
            await asyncio.wait_for(
                redis.ping(),
                timeout=timeout or self._default_timeout,
            )
            latency = (asyncio.get_event_loop().time() - start) * 1000
            return ComponentHealth(status="up", latency_ms=round(latency, 2))

        except TimeoutError:
            logger.warning("redis_health_check_timeout")
            return ComponentHealth(status="down", error="timeout")
        except Exception as e:
            logger.warning("redis_health_check_failed", error=str(e))
            return ComponentHealth(status="down", error=str(e))


# =============================================================================
# Singleton
# =============================================================================

_health_check_service: HealthCheckService | None = None


def get_health_check_service() -> HealthCheckService:
    """取得 HealthCheckService singleton"""
    global _health_check_service
    if _health_check_service is None:
        _health_check_service = HealthCheckService()
    return _health_check_service
