"""Health and sovereignty status endpoints.

Provides system health checks and sovereignty audit information
for the Mission Control dashboard.
"""

import asyncio
import logging
import os
import sqlite3
import time
from contextlib import closing
from datetime import UTC, datetime
from pathlib import Path
from typing import Any

from fastapi import APIRouter, Request
from fastapi.responses import HTMLResponse, JSONResponse
from pydantic import BaseModel

from config import APP_START_TIME as _START_TIME
from config import settings

logger = logging.getLogger(__name__)

router = APIRouter(tags=["health"])

# Shutdown state tracking for graceful shutdown
_shutdown_requested = False
_shutdown_reason: str | None = None
_shutdown_start_time: float | None = None

# Default graceful shutdown timeout (seconds)
GRACEFUL_SHUTDOWN_TIMEOUT = float(os.getenv("GRACEFUL_SHUTDOWN_TIMEOUT", "30"))


def request_shutdown(reason: str = "unknown") -> None:
    """Signal that a graceful shutdown has been requested.

    This is called by signal handlers to inform health checks
    that the service is shutting down.
    """
    global _shutdown_requested, _shutdown_reason, _shutdown_start_time  # noqa: PLW0603
    _shutdown_requested = True
    _shutdown_reason = reason
    _shutdown_start_time = time.monotonic()
    logger.info("Shutdown requested: %s", reason)


def is_shutting_down() -> bool:
    """Check if the service is in the process of shutting down."""
    return _shutdown_requested


def get_shutdown_info() -> dict[str, Any] | None:
    """Get information about the shutdown state, if active."""
    if not _shutdown_requested:
        return None
    elapsed = None
    if _shutdown_start_time:
        elapsed = time.monotonic() - _shutdown_start_time
    return {
        "requested": _shutdown_requested,
        "reason": _shutdown_reason,
        "elapsed_seconds": elapsed,
        "timeout_seconds": GRACEFUL_SHUTDOWN_TIMEOUT,
    }


class DependencyStatus(BaseModel):
    """Status of a single dependency."""

    name: str
    status: str  # "healthy", "degraded", "unavailable"
    sovereignty_score: int  # 0-10
    details: dict[str, Any]


class SovereigntyReport(BaseModel):
    """Full sovereignty audit report."""

    overall_score: float
    dependencies: list[DependencyStatus]
    timestamp: str
    recommendations: list[str]


class HealthStatus(BaseModel):
    """System health status."""

    status: str
    timestamp: str
    version: str
    uptime_seconds: float


class DetailedHealthStatus(BaseModel):
    """Detailed health status with all service checks."""

    status: str  # "healthy", "degraded", "unhealthy"
    timestamp: str
    version: str
    uptime_seconds: float
    services: dict[str, dict[str, Any]]
    system: dict[str, Any]
    shutdown: dict[str, Any] | None = None


class ReadinessStatus(BaseModel):
    """Readiness probe response."""

    ready: bool
    timestamp: str
    checks: dict[str, bool]
    reason: str | None = None


class LivenessStatus(BaseModel):
    """Liveness probe response."""

    alive: bool
    timestamp: str
    uptime_seconds: float
    shutdown_requested: bool = False


# Simple uptime tracking

# Ollama health cache (30-second TTL)
_ollama_cache: DependencyStatus | None = None
_ollama_cache_ts: float = 0.0
_OLLAMA_CACHE_TTL = 30.0


def _check_ollama_sync() -> DependencyStatus:
    """Synchronous Ollama check — run via asyncio.to_thread()."""
    try:
        import urllib.request

        url = settings.normalized_ollama_url
        req = urllib.request.Request(
            f"{url}/api/tags",
            method="GET",
            headers={"Accept": "application/json"},
        )
        with urllib.request.urlopen(req, timeout=2) as response:
            if response.status == 200:
                return DependencyStatus(
                    name="Ollama AI",
                    status="healthy",
                    sovereignty_score=10,
                    details={"url": settings.ollama_url, "model": settings.ollama_model},
                )
    except Exception as exc:
        logger.debug("Ollama health check failed: %s", exc)

    return DependencyStatus(
        name="Ollama AI",
        status="unavailable",
        sovereignty_score=10,
        details={"url": settings.ollama_url, "error": "Cannot connect to Ollama"},
    )


async def _check_ollama() -> DependencyStatus:
    """Check Ollama AI backend status without blocking the event loop.

    Results are cached for 30 seconds to avoid hammering a slow/unreachable
    Ollama instance on every health poll.
    """
    global _ollama_cache, _ollama_cache_ts  # noqa: PLW0603

    now = time.monotonic()
    if _ollama_cache is not None and (now - _ollama_cache_ts) < _OLLAMA_CACHE_TTL:
        return _ollama_cache

    try:
        result = await asyncio.to_thread(_check_ollama_sync)
    except Exception as exc:
        logger.debug("Ollama async check failed: %s", exc)
        result = DependencyStatus(
            name="Ollama AI",
            status="unavailable",
            sovereignty_score=10,
            details={"url": settings.ollama_url, "error": "Cannot connect to Ollama"},
        )

    _ollama_cache = result
    _ollama_cache_ts = now
    return result


async def check_ollama() -> bool:
    """Legacy bool check — used by health_check endpoint."""
    dep = await _check_ollama()
    return dep.status == "healthy"


def _check_lightning() -> DependencyStatus:
    """Check Lightning payment backend status."""
    return DependencyStatus(
        name="Lightning Payments",
        status="unavailable",
        sovereignty_score=8,
        details={"note": "Lightning module removed — will be re-added in v2"},
    )


def _check_sqlite() -> DependencyStatus:
    """Check SQLite database status."""
    try:
        db_path = Path(settings.repo_root) / "data" / "timmy.db"
        with closing(sqlite3.connect(str(db_path))) as conn:
            conn.execute("SELECT 1")

        return DependencyStatus(
            name="SQLite Database",
            status="healthy",
            sovereignty_score=10,
            details={"path": str(db_path)},
        )
    except Exception as exc:
        logger.exception("SQLite health check failed")
        return DependencyStatus(
            name="SQLite Database",
            status="unavailable",
            sovereignty_score=10,
            details={"error": str(exc)},
        )


def _calculate_overall_score(deps: list[DependencyStatus]) -> float:
    """Calculate overall sovereignty score."""
    if not deps:
        return 0.0
    return round(sum(d.sovereignty_score for d in deps) / len(deps), 1)


def _generate_recommendations(deps: list[DependencyStatus]) -> list[str]:
    """Generate recommendations based on dependency status."""
    recommendations = []

    for dep in deps:
        if dep.status == "unavailable":
            recommendations.append(f"{dep.name} is unavailable - check configuration")
        elif dep.status == "degraded":
            if dep.name == "Lightning Payments" and dep.details.get("backend") == "mock":
                recommendations.append(
                    "Switch to real Lightning: set LIGHTNING_BACKEND=lnd and configure LND"
                )

    if not recommendations:
        recommendations.append("System operating optimally - all dependencies healthy")

    return recommendations


@router.get("/health")
async def health_check():
    """Basic health check endpoint.

    Returns legacy format for backward compatibility with existing tests,
    plus extended information for the Mission Control dashboard.
    """
    uptime = (datetime.now(UTC) - _START_TIME).total_seconds()

    # Legacy format for test compatibility
    ollama_ok = await check_ollama()

    agent_status = "idle" if ollama_ok else "offline"

    return {
        "status": "ok" if ollama_ok else "degraded",
        "services": {
            "ollama": "up" if ollama_ok else "down",
        },
        "agents": {
            "agent": {"status": agent_status},
        },
        # Extended fields for Mission Control
        "timestamp": datetime.now(UTC).isoformat(),
        "version": "2.0.0",
        "uptime_seconds": uptime,
        "llm_backend": settings.timmy_model_backend,
        "llm_model": settings.ollama_model,
    }


@router.get("/health/status", response_class=HTMLResponse)
async def health_status_panel(request: Request):
    """Simple HTML health status panel."""
    ollama_ok = await check_ollama()

    status_text = "UP" if ollama_ok else "DOWN"
    status_color = "#10b981" if ollama_ok else "#ef4444"
    import html

    model = html.escape(settings.ollama_model)  # Include model for test compatibility

    html_content = f"""
    <!DOCTYPE html>
    <html>
    <head><title>Health Status</title></head>
    <body style="font-family: monospace; padding: 20px;">
        <h1>System Health</h1>
        <p>Ollama: <span style="color: {status_color}; font-weight: bold;">{status_text}</span></p>
        <p>Model: {model}</p>
        <p>Timestamp: {datetime.now(UTC).isoformat()}</p>
    </body>
    </html>
    """
    return HTMLResponse(content=html_content)


@router.get("/health/sovereignty", response_model=SovereigntyReport)
async def sovereignty_check():
    """Comprehensive sovereignty audit report.

    Returns the status of all external dependencies with sovereignty scores.
    Use this to verify the system is operating in a sovereign manner.
    """
    dependencies = [
        await _check_ollama(),
        _check_lightning(),
        _check_sqlite(),
    ]

    overall = _calculate_overall_score(dependencies)
    recommendations = _generate_recommendations(dependencies)

    return SovereigntyReport(
        overall_score=overall,
        dependencies=dependencies,
        timestamp=datetime.now(UTC).isoformat(),
        recommendations=recommendations,
    )


@router.get("/health/components")
async def component_status():
    """Get status of all system components."""
    return {
        "config": {
            "debug": settings.debug,
            "model_backend": settings.timmy_model_backend,
            "ollama_model": settings.ollama_model,
        },
        "timestamp": datetime.now(UTC).isoformat(),
    }


@router.get("/health/snapshot")
async def health_snapshot():
    """Quick health snapshot before coding.

    Returns a concise status summary including:
    - CI pipeline status (pass/fail/unknown)
    - Critical issues count (P0/P1)
    - Test flakiness rate
    - Token economy temperature

    Fast execution (< 5 seconds) for pre-work checks.
    Refs: #710
    """
    import sys
    from pathlib import Path

    # Import the health snapshot module
    snapshot_path = Path(settings.repo_root) / "timmy_automations" / "daily_run"
    if str(snapshot_path) not in sys.path:
        sys.path.insert(0, str(snapshot_path))

    try:
        from health_snapshot import generate_snapshot, get_token, load_config

        config = load_config()
        token = get_token(config)

        # Run the health snapshot (in thread to avoid blocking)
        snapshot = await asyncio.to_thread(generate_snapshot, config, token)

        return snapshot.to_dict()
    except Exception as exc:
        logger.warning("Health snapshot failed: %s", exc)
        # Return graceful fallback
        return {
            "timestamp": datetime.now(UTC).isoformat(),
            "overall_status": "unknown",
            "error": str(exc),
            "ci": {"status": "unknown", "message": "Snapshot failed"},
            "issues": {"count": 0, "p0_count": 0, "p1_count": 0, "issues": []},
            "flakiness": {
                "status": "unknown",
                "recent_failures": 0,
                "recent_cycles": 0,
                "failure_rate": 0.0,
                "message": "Snapshot failed",
            },
            "tokens": {"status": "unknown", "message": "Snapshot failed"},
        }


# -----------------------------------------------------------------------------
# Production Health Check Endpoints (Readiness & Liveness Probes)
# -----------------------------------------------------------------------------


@router.get("/health/detailed")
async def health_detailed() -> JSONResponse:
    """Comprehensive health check with all service statuses.

    Returns 200 if healthy, 503 if degraded/unhealthy.
    Includes shutdown state for graceful shutdown awareness.
    """
    uptime = (datetime.now(UTC) - _START_TIME).total_seconds()

    # Check all services in parallel
    ollama_dep, sqlite_dep = await asyncio.gather(
        _check_ollama(),
        asyncio.to_thread(_check_sqlite),
    )

    # Build service status map
    services = {
        "ollama": {
            "status": ollama_dep.status,
            "healthy": ollama_dep.status == "healthy",
            "details": ollama_dep.details,
        },
        "sqlite": {
            "status": sqlite_dep.status,
            "healthy": sqlite_dep.status == "healthy",
            "details": sqlite_dep.details,
        },
    }

    # Determine overall status
    all_healthy = all(s["healthy"] for s in services.values())
    any_unhealthy = any(s["status"] == "unavailable" for s in services.values())

    if all_healthy:
        status = "healthy"
        status_code = 200
    elif any_unhealthy:
        status = "unhealthy"
        status_code = 503
    else:
        status = "degraded"
        status_code = 503

    # Add shutdown state if shutting down
    shutdown_info = get_shutdown_info()

    # System info
    import psutil

    try:
        process = psutil.Process()
        memory_info = process.memory_info()
        system = {
            "memory_mb": round(memory_info.rss / (1024 * 1024), 2),
            "cpu_percent": process.cpu_percent(interval=0.1),
            "threads": process.num_threads(),
        }
    except Exception as exc:
        logger.debug("Could not get system info: %s", exc)
        system = {"error": "unavailable"}

    response_data = {
        "status": status,
        "timestamp": datetime.now(UTC).isoformat(),
        "version": "2.0.0",
        "uptime_seconds": uptime,
        "services": services,
        "system": system,
    }

    if shutdown_info:
        response_data["shutdown"] = shutdown_info
        # Force 503 if shutting down
        status_code = 503

    return JSONResponse(content=response_data, status_code=status_code)


@router.get("/ready")
async def readiness_probe() -> JSONResponse:
    """Readiness probe for Kubernetes/Docker.

    Returns 200 when the service is ready to receive traffic.
    Returns 503 during startup or shutdown.
    """
    uptime = (datetime.now(UTC) - _START_TIME).total_seconds()

    # Minimum uptime before ready (allow startup to complete)
    MIN_READY_UPTIME = 5.0

    checks = {
        "startup_complete": uptime >= MIN_READY_UPTIME,
        "database": False,
        "not_shutting_down": not is_shutting_down(),
    }

    # Check database connectivity
    try:
        db_path = Path(settings.repo_root) / "data" / "timmy.db"
        if db_path.exists():
            with closing(sqlite3.connect(str(db_path))) as conn:
                conn.execute("SELECT 1")
            checks["database"] = True
    except Exception as exc:
        logger.debug("Readiness DB check failed: %s", exc)

    ready = all(checks.values())

    response_data = {
        "ready": ready,
        "timestamp": datetime.now(UTC).isoformat(),
        "checks": checks,
    }

    if not ready and is_shutting_down():
        response_data["reason"] = f"Service shutting down: {_shutdown_reason}"

    status_code = 200 if ready else 503
    return JSONResponse(content=response_data, status_code=status_code)


@router.get("/live")
async def liveness_probe() -> JSONResponse:
    """Liveness probe for Kubernetes/Docker.

    Returns 200 if the service is alive and functioning.
    Returns 503 if the service is deadlocked or should be restarted.
    """
    uptime = (datetime.now(UTC) - _START_TIME).total_seconds()

    # Basic liveness: we respond, so we're alive
    alive = True

    # If shutting down and past timeout, report not alive to force restart
    if is_shutting_down() and _shutdown_start_time:
        elapsed = time.monotonic() - _shutdown_start_time
        if elapsed > GRACEFUL_SHUTDOWN_TIMEOUT:
            alive = False
            logger.warning("Liveness probe failed: shutdown timeout exceeded")

    response_data = {
        "alive": alive,
        "timestamp": datetime.now(UTC).isoformat(),
        "uptime_seconds": uptime,
        "shutdown_requested": is_shutting_down(),
    }

    status_code = 200 if alive else 503
    return JSONResponse(content=response_data, status_code=status_code)


@router.get("/health/shutdown", include_in_schema=False)
async def shutdown_status() -> JSONResponse:
    """Get shutdown status (internal/debug endpoint).

    Returns shutdown state information for debugging graceful shutdown.
    """
    shutdown_info = get_shutdown_info()

    response_data = {
        "shutting_down": is_shutting_down(),
        "timestamp": datetime.now(UTC).isoformat(),
    }

    if shutdown_info:
        response_data.update(shutdown_info)

    return JSONResponse(content=response_data)
