#!/usr/bin/env python3
"""
Genesis Health Check API
=========================
System health check endpoint for monitoring.

PM-049: Health Check API
- Checks system component health
- Returns status and metrics
- Supports liveness and readiness probes
"""

import os
import logging
import socket
import psutil
from typing import Dict, Any, List, Optional
from datetime import datetime
from dataclasses import dataclass, asdict

# Optional Flask import
try:
    from flask import Flask, jsonify
    FLASK_AVAILABLE = True
except ImportError:
    FLASK_AVAILABLE = False

logger = logging.getLogger("HealthCheckAPI")
logging.basicConfig(level=logging.INFO)


@dataclass
class ComponentHealth:
    """Health status of a component."""
    name: str
    status: str  # healthy, degraded, unhealthy
    latency_ms: float
    last_check: str
    details: Dict[str, Any]

    def to_dict(self) -> Dict[str, Any]:
        return asdict(self)


class HealthCheckAPI:
    """
    Health check API for Genesis system monitoring.
    Provides liveness, readiness, and component health endpoints.
    """

    def __init__(self, components: Dict[str, Any] = None):
        """
        Initialize health check API.

        Args:
            components: Dict of component name to health check function
        """
        self.components = components or {}
        self.start_time = datetime.utcnow()
        self.health_history: List[Dict[str, Any]] = []

        logger.info("Health Check API initialized")

    def _check_component(self, name: str, check_fn) -> ComponentHealth:
        """Run health check for a component."""
        start = datetime.utcnow()

        try:
            result = check_fn()
            status = "healthy" if result.get("healthy", True) else "unhealthy"
            details = result
        except Exception as e:
            status = "unhealthy"
            details = {"error": str(e)}

        latency = (datetime.utcnow() - start).total_seconds() * 1000

        return ComponentHealth(
            name=name,
            status=status,
            latency_ms=round(latency, 2),
            last_check=datetime.utcnow().isoformat(),
            details=details
        )

    def register_component(self, name: str, check_fn) -> None:
        """Register a component health check function."""
        self.components[name] = check_fn
        logger.info(f"Registered health check for: {name}")

    # ===== BUILT-IN CHECKS =====

    def check_system(self) -> Dict[str, Any]:
        """Check system resources."""
        try:
            cpu_percent = psutil.cpu_percent(interval=0.1)
            memory = psutil.virtual_memory()
            disk = psutil.disk_usage("/")

            return {
                "healthy": cpu_percent < 90 and memory.percent < 90,
                "cpu_percent": cpu_percent,
                "memory_percent": memory.percent,
                "memory_available_gb": round(memory.available / (1024**3), 2),
                "disk_percent": disk.percent,
                "disk_free_gb": round(disk.free / (1024**3), 2)
            }
        except Exception as e:
            return {"healthy": False, "error": str(e)}

    def check_network(self) -> Dict[str, Any]:
        """Check network connectivity."""
        try:
            # Check DNS resolution
            socket.gethostbyname("api.anthropic.com")

            # Check local socket
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            s.settimeout(1)
            s.close()

            return {"healthy": True, "dns_ok": True, "socket_ok": True}
        except Exception as e:
            return {"healthy": False, "error": str(e)}

    def check_redis(self) -> Dict[str, Any]:
        """Check Redis connectivity."""
        try:
            import redis
            r = redis.Redis(
                host=os.getenv("REDIS_HOST", "localhost"),
                port=int(os.getenv("REDIS_PORT", 6379)),
                socket_timeout=2
            )
            r.ping()
            return {"healthy": True, "connected": True}
        except ImportError:
            return {"healthy": True, "available": False, "message": "Redis client not installed"}
        except Exception as e:
            return {"healthy": False, "error": str(e)}

    def check_postgres(self) -> Dict[str, Any]:
        """Check PostgreSQL connectivity."""
        try:
            import psycopg2
            conn = psycopg2.connect(
                host=os.getenv("POSTGRES_HOST", "localhost"),
                port=int(os.getenv("POSTGRES_PORT", 5432)),
                user=os.getenv("POSTGRES_USER", "postgres"),
                password=os.getenv("POSTGRES_PASSWORD", ""),
                database=os.getenv("POSTGRES_DB", "genesis"),
                connect_timeout=3
            )
            conn.close()
            return {"healthy": True, "connected": True}
        except ImportError:
            return {"healthy": True, "available": False, "message": "psycopg2 not installed"}
        except Exception as e:
            return {"healthy": False, "error": str(e)}

    def check_qdrant(self) -> Dict[str, Any]:
        """Check Qdrant vector DB connectivity."""
        try:
            from qdrant_client import QdrantClient
            client = QdrantClient(
                host=os.getenv("QDRANT_HOST", "localhost"),
                port=int(os.getenv("QDRANT_PORT", 6333)),
                timeout=3
            )
            collections = client.get_collections()
            return {
                "healthy": True,
                "connected": True,
                "collections": len(collections.collections)
            }
        except ImportError:
            return {"healthy": True, "available": False, "message": "qdrant-client not installed"}
        except Exception as e:
            return {"healthy": False, "error": str(e)}

    def setup_default_checks(self) -> None:
        """Register default health checks."""
        self.register_component("system", self.check_system)
        self.register_component("network", self.check_network)
        self.register_component("redis", self.check_redis)
        self.register_component("postgres", self.check_postgres)
        self.register_component("qdrant", self.check_qdrant)

    # ===== HEALTH ENDPOINTS =====

    def liveness(self) -> Dict[str, Any]:
        """
        Liveness probe - basic check if service is running.

        Returns:
            Dict with status (always healthy if service is running)
        """
        return {
            "status": "healthy",
            "timestamp": datetime.utcnow().isoformat()
        }

    def readiness(self) -> Dict[str, Any]:
        """
        Readiness probe - check if service can handle requests.

        Returns:
            Dict with status and component health
        """
        component_health = {}
        all_healthy = True

        for name, check_fn in self.components.items():
            health = self._check_component(name, check_fn)
            component_health[name] = health.to_dict()

            if health.status != "healthy":
                all_healthy = False

        result = {
            "status": "ready" if all_healthy else "not_ready",
            "timestamp": datetime.utcnow().isoformat(),
            "components": component_health
        }

        # Store in history
        self.health_history.append({
            "timestamp": result["timestamp"],
            "status": result["status"]
        })

        return result

    def full_health(self) -> Dict[str, Any]:
        """
        Full health check with all details.

        Returns:
            Comprehensive health status
        """
        readiness = self.readiness()
        uptime = (datetime.utcnow() - self.start_time).total_seconds()

        return {
            "status": readiness["status"],
            "timestamp": readiness["timestamp"],
            "uptime_seconds": round(uptime, 2),
            "uptime_formatted": self._format_uptime(uptime),
            "version": os.getenv("GENESIS_VERSION", "1.0.0"),
            "environment": os.getenv("GENESIS_ENV", "development"),
            "components": readiness["components"],
            "recent_checks": self.health_history[-10:]
        }

    def _format_uptime(self, seconds: float) -> str:
        """Format uptime in human-readable format."""
        days = int(seconds // 86400)
        hours = int((seconds % 86400) // 3600)
        minutes = int((seconds % 3600) // 60)

        parts = []
        if days > 0:
            parts.append(f"{days}d")
        if hours > 0:
            parts.append(f"{hours}h")
        if minutes > 0:
            parts.append(f"{minutes}m")

        return " ".join(parts) or "< 1m"

    def get_component_health(self, component: str) -> Dict[str, Any]:
        """Get health status of a specific component."""
        if component not in self.components:
            return {"error": f"Unknown component: {component}"}

        health = self._check_component(component, self.components[component])
        return health.to_dict()

    def get_metrics(self) -> Dict[str, Any]:
        """Get health check metrics."""
        healthy_count = sum(
            1 for h in self.health_history
            if h["status"] in ["ready", "healthy"]
        )
        total = len(self.health_history)

        return {
            "total_checks": total,
            "healthy_checks": healthy_count,
            "uptime_percent": round(healthy_count / total * 100, 2) if total > 0 else 100,
            "components_registered": len(self.components),
            "last_check": self.health_history[-1] if self.health_history else None
        }

    # ===== FLASK APP FACTORY =====

    def create_flask_app(self) -> Optional[Any]:
        """Create Flask app with health endpoints."""
        if not FLASK_AVAILABLE:
            logger.warning("Flask not available.")
            return None

        app = Flask(__name__)
        health = self

        @app.route("/health", methods=["GET"])
        @app.route("/health/live", methods=["GET"])
        def liveness():
            return jsonify(health.liveness())

        @app.route("/health/ready", methods=["GET"])
        def readiness():
            result = health.readiness()
            status_code = 200 if result["status"] == "ready" else 503
            return jsonify(result), status_code

        @app.route("/health/full", methods=["GET"])
        def full():
            return jsonify(health.full_health())

        @app.route("/health/component/<name>", methods=["GET"])
        def component(name):
            return jsonify(health.get_component_health(name))

        @app.route("/health/metrics", methods=["GET"])
        def metrics():
            return jsonify(health.get_metrics())

        return app


# Global instance
_health_api: Optional[HealthCheckAPI] = None


def get_health_api() -> HealthCheckAPI:
    """Get or create global health check API."""
    global _health_api
    if _health_api is None:
        _health_api = HealthCheckAPI()
        _health_api.setup_default_checks()
    return _health_api


if __name__ == "__main__":
    # Self-test
    api = HealthCheckAPI()
    api.setup_default_checks()

    print("\n=== Health Check API Test ===")

    # Liveness
    print(f"\nLiveness: {api.liveness()}")

    # Readiness
    print(f"\nReadiness: {api.readiness()}")

    # Full health
    import json
    print(f"\nFull Health: {json.dumps(api.full_health(), indent=2)}")

    # Metrics
    print(f"\nMetrics: {api.get_metrics()}")
