"""
Monitoring Status API
=====================
探測所有可觀測性工具狀態：
  Grafana / Prometheus / Sentry / Langfuse / SigNoz / Gitea

所有探測從後端發出，不暴露內網 IP 給前端。
Grafana: 110:3002 (Docker 3002→3000)

建立時間: 2026-04-03 (台北時區)
建立者: Claude Code
更新時間: 2026-04-03 加入 Grafana(3002) / Sentry / Langfuse
"""

import asyncio
from datetime import UTC, datetime

import httpx
from fastapi import APIRouter

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

logger = get_logger(__name__)

router = APIRouter(prefix="/monitoring", tags=["Monitoring"])

TIMEOUT = 3.0


# =============================================================================
# Probes
# =============================================================================

async def _probe_grafana(client: httpx.AsyncClient) -> dict:
    base = "http://192.168.0.110:3002"
    try:
        r = await client.get(f"{base}/api/health", timeout=TIMEOUT)
        if r.status_code == 200:
            data = r.json()
            version = data.get("version")
            dash_count = None
            grafana_api_key = settings.GRAFANA_API_KEY.strip()
            if grafana_api_key and grafana_api_key != "CHANGE_ME":
                dash_r = await client.get(
                    f"{base}/api/search?type=dash-db",
                    headers={"Authorization": f"Bearer {grafana_api_key}"},
                    timeout=TIMEOUT,
                )
                if dash_r.status_code == 200 and isinstance(dash_r.json(), list):
                    dash_count = len(dash_r.json())
            return {
                "name": "Grafana",
                "status": "up",
                "version": version,
                "stats": f"面板 {dash_count} 個" if dash_count is not None else "監控面板",
                "description": "指標視覺化 · Dashboard",
                "url": base,
            }
    except Exception as e:
        logger.warning("grafana_probe_failed", error=str(e))
    return {
        "name": "Grafana", "status": "down", "version": None,
        "stats": None, "description": "指標視覺化 · Dashboard", "url": base,
    }


async def _probe_prometheus(client: httpx.AsyncClient) -> dict:
    # 2026-04-29 ogt + Claude Opus 4.7: 改用 settings 對齊單一事實源
    # 原本寫死 110:9090 雖巧合正確，但繞過 ConfigMap 注入機制
    base = settings.PROMETHEUS_URL
    try:
        health_r = await client.get(f"{base}/-/healthy", timeout=TIMEOUT)
        if health_r.status_code == 200:
            build_r = await client.get(f"{base}/api/v1/status/buildinfo", timeout=TIMEOUT)
            version = None
            if build_r.status_code == 200:
                version = build_r.json().get("data", {}).get("version")
            rules_r = await client.get(f"{base}/api/v1/rules", timeout=TIMEOUT)
            rules_count = 0
            firing_count = 0
            if rules_r.status_code == 200:
                groups = rules_r.json().get("data", {}).get("groups", [])
                rules_count = sum(len(g.get("rules", [])) for g in groups)
                firing_count = sum(
                    1 for g in groups for r in g.get("rules", [])
                    if r.get("state") == "firing"
                )
            stats_parts = [f"規則 {rules_count} 條"]
            if firing_count > 0:
                stats_parts.append(f"{firing_count} 觸發")
            return {
                "name": "Prometheus",
                "status": "up",
                "version": version,
                "stats": " · ".join(stats_parts),
                "description": "時序資料庫 · 告警規則",
                "firing_count": firing_count,
                "url": base,
            }
    except Exception as e:
        logger.warning("prometheus_probe_failed", error=str(e))
    return {
        "name": "Prometheus", "status": "down", "version": None,
        "stats": None, "description": "時序資料庫 · 告警規則", "firing_count": 0, "url": base,
    }


async def _probe_sentry(client: httpx.AsyncClient) -> dict:
    base = "http://192.168.0.110:9000"
    try:
        r = await client.get(f"{base}/_health/", timeout=TIMEOUT)
        if r.status_code == 200 and r.text.strip() == "ok":
            ver_r = await client.get(f"{base}/api/0/", timeout=TIMEOUT)
            version = None
            if ver_r.status_code == 200:
                raw = ver_r.json().get("version")
                if isinstance(raw, dict):
                    version = raw.get("version")
                elif raw:
                    version = str(raw)
            return {
                "name": "Sentry",
                "status": "up",
                "version": version,
                "stats": "Error Tracking · Issue",
                "description": "錯誤追蹤 · Issue 管理",
                "url": base,
            }
    except Exception as e:
        logger.warning("sentry_probe_failed", error=str(e))
    return {
        "name": "Sentry", "status": "down", "version": None,
        "stats": None, "description": "錯誤追蹤 · Issue 管理", "url": base,
    }


async def _probe_langfuse(client: httpx.AsyncClient) -> dict:
    base = "http://192.168.0.110:3100"
    try:
        r = await client.get(f"{base}/api/public/health", timeout=TIMEOUT)
        if r.status_code == 200:
            data = r.json()
            version = data.get("version")
            return {
                "name": "Langfuse",
                "status": "up",
                "version": version,
                "stats": "LLM Tracing · AI 觀測",
                "description": "LLM 追蹤 · AI 成本監控",
                "url": base,
            }
    except Exception as e:
        logger.warning("langfuse_probe_failed", error=str(e))
    return {
        "name": "Langfuse", "status": "down", "version": None,
        "stats": None, "description": "LLM 追蹤 · AI 成本監控", "url": base,
    }


async def _probe_signoz(client: httpx.AsyncClient) -> dict:
    base = "http://192.168.0.188:3301"
    try:
        r = await client.get(f"{base}/api/v1/health", timeout=TIMEOUT)
        if r.status_code == 200:
            return {
                "name": "SigNoz",
                "status": "up",
                "version": None,
                "stats": "APM · Trace · Log",
                "description": "可觀測性平台 · OTEL",
                "url": base,
            }
    except Exception as e:
        logger.warning("signoz_probe_failed", error=str(e))
    try:
        r2 = await client.get(f"{base}/", timeout=TIMEOUT)
        if r2.status_code in (200, 301, 302):
            return {
                "name": "SigNoz", "status": "up", "version": None,
                "stats": "APM · Trace · Log", "description": "可觀測性平台 · OTEL", "url": base,
            }
    except Exception:
        pass
    return {
        "name": "SigNoz", "status": "down", "version": None,
        "stats": None, "description": "可觀測性平台 · OTEL", "url": base,
    }


async def _probe_gitea(client: httpx.AsyncClient) -> dict:
    base = "http://192.168.0.110:3001"
    try:
        # Use /api/v1/version — /-/readiness returns 404 on this Gitea version
        ver_r = await client.get(f"{base}/api/v1/version", timeout=TIMEOUT)
        if ver_r.status_code == 200:
            version = ver_r.json().get("version")
            return {
                "name": "Gitea",
                "status": "up",
                "version": version,
                "stats": "CI/CD · Git · Mirror",
                "description": "代碼倉庫 · Pipeline",
                "url": base,
            }
    except Exception as e:
        logger.warning("gitea_probe_failed", error=str(e))
    return {
        "name": "Gitea", "status": "down", "version": None,
        "stats": None, "description": "代碼倉庫 · Pipeline", "url": base,
    }


# =============================================================================
# Router
# =============================================================================

@router.get("/status")
async def get_monitoring_status() -> dict:
    """
    並行探測所有可觀測性工具狀態

    工具清單: Grafana(3002) / Prometheus / Sentry / Langfuse / SigNoz / Gitea
    注意: Loki 未安裝 (ADR: SigNoz 統一派)

    Returns:
        dict with tools list, each containing name/status/version/stats/description
    """
    async with httpx.AsyncClient(follow_redirects=True) as client:
        results = await asyncio.gather(
            _probe_grafana(client),
            _probe_prometheus(client),
            _probe_sentry(client),
            _probe_langfuse(client),
            _probe_signoz(client),
            _probe_gitea(client),
            return_exceptions=True,
        )

    now = datetime.now(UTC).isoformat()
    tools = []
    for r in results:
        if isinstance(r, Exception):
            logger.error("monitoring_probe_exception", error=str(r))
            continue
        tools.append({**r, "checked_at": now})

    return {
        "tools": tools,
        "checked_at": now,
    }
