"""
arifosmcp/runtime/webmcp/live_metrics.py — Real-time Dashboard Data Feed

Three data groups:
- MACHINE: VPS health (CPU, RAM, disk, network, Docker)
- GOVERNANCE: arifOS constitutional metrics (F1-F13, vitals, VAULT999)
- INTELLIGENCE: AI/LLM metrics (tokens, latency, model status, cost)

This module provides live data aggregation for the APEX dashboard.
"""

from __future__ import annotations

import asyncio
import json
import os
import time
from dataclasses import asdict, dataclass, field
from pathlib import Path
from typing import Any

import httpx

from arifosmcp.runtime.floor import get_floor_count

try:
    import psutil

    PSUTIL_AVAILABLE = True
except ImportError:
    PSUTIL_AVAILABLE = False
    psutil = None


@dataclass
class MachineMetrics:
    """VPS machine health metrics."""

    timestamp: float = field(default_factory=time.time)

    # CPU
    cpu_percent: float = 0.0
    cpu_count: int = 0
    cpu_freq_mhz: float = 0.0
    load_avg: list[float] = field(default_factory=list)

    # Memory
    ram_percent: float = 0.0
    ram_used_gb: float = 0.0
    ram_total_gb: float = 0.0
    swap_percent: float = 0.0

    # Disk
    disk_percent: float = 0.0
    disk_used_gb: float = 0.0
    disk_total_gb: float = 0.0

    # Network
    net_io_sent_mb: float = 0.0
    net_io_recv_mb: float = 0.0
    connections: int = 0

    # Docker
    docker_containers: int = 0
    docker_running: int = 0
    docker_images: int = 0

    # Uptime
    uptime_seconds: float = 0.0
    boot_time: float = 0.0


@dataclass
class GovernanceMetrics:
    """arifOS constitutional governance metrics."""

    timestamp: float = field(default_factory=time.time)

    # 13 Floors status
    floors: dict[str, dict] = field(default_factory=dict)
    floors_active: int = field(default_factory=get_floor_count)
    floors_passing: int = 13
    floors_failing: int = 0

    # Vitals (thermodynamics)
    G_star: float = 0.85
    entropy_delta: float = -0.3
    peace2: float = 1.05
    omega: float = 0.04
    kappa_r: float = 0.97
    psi_le: float = 1.09

    # Tri-Witness
    witness_human: float = 1.0
    witness_ai: float = 0.97
    witness_earth: float = 0.91
    witness_consensus: float = 0.95

    # System status
    system_status: str = "HEALTHY"
    governance_mode: str = "HARD"
    version: str = "2026.03.14-VALIDATED"

    # VAULT999 summary
    vault_total_entries: int = 0
    vault_last_seal: str = ""
    vault_chain_integrity: bool = True

    # Session metrics
    active_sessions: int = 0
    total_requests: int = 0
    seal_rate: float = 0.97
    avg_latency_ms: float = 45.0


@dataclass
class IntelligenceMetrics:
    """AI/LLM intelligence metrics."""

    timestamp: float = field(default_factory=time.time)

    # Token usage
    tokens_input: int = 0
    tokens_output: int = 0
    tokens_total: int = 0

    # Cost tracking
    cost_usd: float = 0.0
    cost_per_1k_tokens: float = 0.0

    # Latency
    avg_latency_ms: float = 0.0
    p50_latency_ms: float = 0.0
    p95_latency_ms: float = 0.0
    p99_latency_ms: float = 0.0

    # Model status
    models: list[dict] = field(default_factory=list)
    active_model: str = ""

    # Provider health
    ollama_status: str = "unknown"
    ollama_models: int = 0
    openai_status: str = "unknown"
    anthropic_status: str = "unknown"

    # Reasoning metrics
    reasoning_steps: int = 0
    tool_calls: int = 0
    cache_hits: int = 0
    cache_misses: int = 0


class LiveMetricsCollector:
    """Collects real-time metrics from all three data groups."""

    def __init__(self):
        self._cache: dict[str, Any] = {}
        self._cache_time: dict[str, float] = {}
        self._cache_ttl = 5.0  # 5 second cache

    async def get_machine_metrics(self) -> MachineMetrics:
        """Collect VPS machine metrics."""
        if not PSUTIL_AVAILABLE:
            return MachineMetrics(cpu_percent=0.0, system_status="DEGRADED: psutil unavailable")

        try:
            # CPU metrics
            cpu_percent = psutil.cpu_percent(interval=0.1)
            cpu_count = psutil.cpu_count()
            cpu_freq = psutil.cpu_freq()

            # Load average (Unix only)
            try:
                load_avg = list(os.getloadavg()) if hasattr(os, "getloadavg") else [0.0, 0.0, 0.0]
            except Exception:
                load_avg = [0.0, 0.0, 0.0]

            # Memory
            mem = psutil.virtual_memory()
            swap = psutil.swap_memory()

            # Disk
            disk = psutil.disk_usage("/")

            # Network
            net_io = psutil.net_io_counters()
            connections = len(psutil.net_connections())

            # Uptime
            boot_time = psutil.boot_time()
            uptime = time.time() - boot_time

            # Docker (optional)
            docker_containers = 0
            docker_running = 0
            docker_images = 0
            try:
                import docker

                client = docker.from_env()
                containers = client.containers.list(all=True)
                docker_containers = len(containers)
                docker_running = len([c for c in containers if c.status == "running"])
                docker_images = len(client.images.list())
            except Exception:
                pass

            return MachineMetrics(
                cpu_percent=cpu_percent,
                cpu_count=cpu_count,
                cpu_freq_mhz=cpu_freq.current if cpu_freq else 0,
                load_avg=load_avg,
                ram_percent=mem.percent,
                ram_used_gb=mem.used / (1024**3),
                ram_total_gb=mem.total / (1024**3),
                swap_percent=swap.percent,
                disk_percent=disk.percent,
                disk_used_gb=disk.used / (1024**3),
                disk_total_gb=disk.total / (1024**3),
                net_io_sent_mb=net_io.bytes_sent / (1024**2),
                net_io_recv_mb=net_io.bytes_recv / (1024**2),
                connections=connections,
                docker_containers=docker_containers,
                docker_running=docker_running,
                docker_images=docker_images,
                uptime_seconds=uptime,
                boot_time=boot_time,
            )
        except Exception as e:
            # Return degraded metrics on error
            return MachineMetrics(cpu_percent=0.0, system_status=f"ERROR: {str(e)[:50]}")

    async def get_governance_metrics(self) -> GovernanceMetrics:
        """Collect arifOS governance metrics."""
        try:
            # import core modules
            from core.shared.floors import THRESHOLDS

            from core.physics.thermodynamics_hardened import get_thermodynamic_report

            try:
                from core.state.session_manager import session_manager
            except Exception:
                session_manager = None

            # Get floor statuses
            floors = {}
            floors_passing = 0
            floors_failing = 0

            for floor_id, config in THRESHOLDS.items():
                # Check if floor is currently passing (this would come from actual checks)
                # For now, use healthy defaults
                is_passing = True
                status = "pass"

                if floor_id in ["F10", "F11"]:
                    status = "lock"
                elif floor_id == "F13":
                    status = "human"

                floors[floor_id] = {
                    "name": config.get("name", floor_id),
                    "type": config.get("type", "Hard"),
                    "threshold": str(config.get("threshold", "N/A")),
                    "status": status,
                    "score": 0.95 if is_passing else 0.5,
                }

                if is_passing:
                    floors_passing += 1
                else:
                    floors_failing += 1

            # Get thermodynamic vitals
            try:
                thermo = get_thermodynamic_report()
                G_star = thermo.get("G_star", 0.85)
                entropy_delta = thermo.get("entropy_delta", -0.3)
                peace2 = thermo.get("peace2", 1.05)
                omega = thermo.get("omega", 0.04)
                kappa_r = thermo.get("kappa_r", 0.97)
                psi_le = thermo.get("psi_le", 1.09)
            except Exception:
                G_star, entropy_delta, peace2, omega, kappa_r, psi_le = (
                    0.85,
                    -0.3,
                    1.05,
                    0.04,
                    0.97,
                    1.09,
                )

            # Get VAULT999 stats
            vault_entries = 0
            vault_last = ""
            try:
                dsn = os.environ.get("DATABASE_URL") or os.environ.get("POSTGRES_URL")
                if dsn:
                    import asyncpg

                    conn = await asyncpg.connect(dsn)
                    row = await conn.fetchrow(
                        "SELECT COUNT(*)::int AS n, MAX(timestamp)::text AS last_timestamp "
                        "FROM arifosmcp_vault_seals"
                    )
                    vault_entries = row["n"] if row and row["n"] is not None else 0
                    vault_last = row["last_timestamp"] if row and row["last_timestamp"] else ""
                    await conn.close()
                else:
                    vault_path = Path("VAULT999/vault999.jsonl")
                    if vault_path.exists():
                        with open(vault_path) as f:
                            lines = f.readlines()
                            vault_entries = len(lines)
                            if lines:
                                last = json.loads(lines[-1])
                                vault_last = last.get("timestamp", "")
            except Exception:
                pass

            # Get session count
            active_sessions = 0
            try:
                if session_manager is not None:
                    active_sessions = len(session_manager.list_sessions())
            except Exception:
                pass

            return GovernanceMetrics(
                floors=floors,
                floors_active=len(floors),
                floors_passing=floors_passing,
                floors_failing=floors_failing,
                G_star=G_star,
                entropy_delta=entropy_delta,
                peace2=peace2,
                omega=omega,
                kappa_r=kappa_r,
                psi_le=psi_le,
                vault_total_entries=vault_entries,
                vault_last_seal=vault_last,
                active_sessions=active_sessions,
            )

        except Exception as e:
            return GovernanceMetrics(
                system_status=f"DEGRADED: {str(e)[:50]}",
                floors={f"F{i}": {"status": "unknown", "score": 0} for i in range(1, 14)},
            )

    async def get_intelligence_metrics(self) -> IntelligenceMetrics:
        """Collect AI/LLM intelligence metrics."""
        metrics = IntelligenceMetrics()

        try:
            # Check Ollama
            async with httpx.AsyncClient(timeout=5.0) as client:
                try:
                    resp = await client.get("http://localhost:11434/api/tags")
                    if resp.status_code == 200:
                        data = resp.json()
                        metrics.ollama_status = "healthy"
                        metrics.ollama_models = len(data.get("models", []))
                        metrics.models = [
                            {"name": m.get("name"), "provider": "ollama"}
                            for m in data.get("models", [])[:5]
                        ]
                except Exception:
                    metrics.ollama_status = "unavailable"

            # Get runtime metrics from arifOS
            try:
                from arifosmcp.runtime.metrics import live_metrics

                global_status = live_metrics.get_global_status()
                metrics.tool_calls = global_status.get("total_recorded_errors", 0)  # Placeholder
            except Exception:
                pass

            # TODO: Integrate with actual LLM provider APIs for token counts
            # For now, return structure with zeros

        except Exception as e:
            metrics.ollama_status = f"error: {str(e)[:30]}"

        return metrics

    async def get_all_metrics(self) -> dict[str, Any]:
        """Get all three metric groups."""
        machine, governance, intelligence = await asyncio.gather(
            self.get_machine_metrics(),
            self.get_governance_metrics(),
            self.get_intelligence_metrics(),
        )

        return {
            "machine": asdict(machine),
            "governance": asdict(governance),
            "intelligence": asdict(intelligence),
            "timestamp": time.time(),
            "verdict": "SEAL",
        }


# Global collector instance
collector = LiveMetricsCollector()


async def get_live_metrics() -> dict[str, Any]:
    """Public API to get all live metrics."""
    return await collector.get_all_metrics()


async def get_machine_only() -> dict[str, Any]:
    """Get machine metrics only."""
    return asdict(await collector.get_machine_metrics())


async def get_governance_only() -> dict[str, Any]:
    """Get governance metrics only."""
    return asdict(await collector.get_governance_metrics())


async def get_intelligence_only() -> dict[str, Any]:
    """Get intelligence metrics only."""
    return asdict(await collector.get_intelligence_metrics())
