#!/usr/bin/env python3
# Copyright (c) 2026 PULSE Contributors. Proprietary. All rights reserved.
# See LICENSE file for details.
"""
PULSE API — System router. Endpoints for system status (orchestrator, daemons, workers, task board).
"""
from __future__ import annotations

import json
import sys
from pathlib import Path
from fastapi import APIRouter

from pulse.core.brain_utils import EVIDENCE_DIR

SCRIPT_DIR = Path(__file__).resolve().parent
ROOT_DIR = SCRIPT_DIR.parent.parent
STATE_DIR = ROOT_DIR / "state"
TASK_BOARD = STATE_DIR / "task_board.json"
PID_FILE = STATE_DIR / "pulse.pid"
DAEMON_PIDS = STATE_DIR / "daemon_pids.json"
WORKER_PIDS = STATE_DIR / "worker_pids.json"


router = APIRouter(prefix="/v1/system", tags=["system"])


def _is_process_running(pid) -> bool:
    """Check if process with given PID is running (Windows-safe)."""
    try:
        pid = int(pid)
    except (TypeError, ValueError):
        return False
    if pid <= 0:
        return False
    try:
        import os
        os.kill(pid, 0)
        return True
    except Exception:
        return False


def _load_json(filepath: Path) -> dict:
    """Load JSON file, return empty dict on error."""
    if not filepath.exists():
        return {}
    try:
        return json.loads(filepath.read_text(encoding="utf-8"))
    except Exception:
        return {}


@router.get("/overview")
async def get_system_overview():
    """Return complete system overview: orchestrator, daemons, workers, task board."""
    # Orchestrator status
    orch_pid = None
    orch_running = False
    if PID_FILE.exists():
        try:
            orch_pid = int(PID_FILE.read_text().strip())
            orch_running = _is_process_running(orch_pid)
        except Exception:
            pass

    # Daemon status
    daemons_alive = 0
    daemons_dead = 0
    try:
        daemon_pids = []
        if DAEMON_PIDS.exists():
            daemon_pids = json.loads(DAEMON_PIDS.read_text(encoding="utf-8"))
        if isinstance(daemon_pids, list):
            for p in daemon_pids:
                try:
                    if _is_process_running(p):
                        daemons_alive += 1
                    else:
                        daemons_dead += 1
                except Exception:
                    daemons_dead += 1
    except Exception:
        pass

    # Worker status
    worker_records = []
    if WORKER_PIDS.exists():
        try:
            worker_records = json.loads(WORKER_PIDS.read_text())
            if not isinstance(worker_records, list):
                worker_records = []
        except Exception:
            pass
    workers_alive = len([r for r in worker_records if _is_process_running(r.get("pid", 0))])
    workers_dead = len(worker_records) - workers_alive

    # Task board summary
    board = _load_json(TASK_BOARD)
    dispatches = board.get("dispatches", [])
    tasks_open = tasks_claimed = tasks_active = tasks_done = tasks_failed = tasks_total = 0

    for d in dispatches:
        for t in d.get("tasks", []):
            tasks_total += 1
            status = t.get("status", "")
            if status == "open":
                tasks_open += 1
            elif status == "claimed":
                tasks_claimed += 1
            elif status == "active":
                tasks_active += 1
            elif status in ("done", "completed", "archived"):
                tasks_done += 1
            elif status == "failed":
                tasks_failed += 1

    # Brain health stats — SQLite-first, JSONL fallback
    brain_stats = {}
    _sqlite_ok = False
    try:
        from pulse.core.brain_db import get_conn
        _gconn = get_conn()
        for label, table in [("lessons", "lessons"), ("failures", "failures"), ("patterns", "patterns")]:
            row = _gconn.execute(f"SELECT COUNT(*) as c FROM {table}").fetchone()
            brain_stats[label] = row["c"] if row else 0
        _sqlite_ok = True
    except Exception:
        pass
    if not _sqlite_ok:
        for label, jsonl_name in [
            ("lessons", "Lessons/auto_lessons.jsonl"),
            ("failures", "Failures/auto_fails.jsonl"),
            ("patterns", "Patterns/auto_patterns.jsonl"),
        ]:
            jsonl_path = ROOT_DIR / "Hippocampus" / jsonl_name
            if jsonl_path.exists():
                try:
                    count = 0
                    with open(jsonl_path, "r", encoding="utf-8") as f:
                        for line in f:
                            if line.strip():
                                count += 1
                    brain_stats[label] = count
                except Exception:
                    brain_stats[label] = 0

    # Evidence count
    evidence_count = 0
    if EVIDENCE_DIR.exists():
        for f in EVIDENCE_DIR.rglob("*.json"):
            try:
                data = json.loads(f.read_text(encoding="utf-8"))
                if isinstance(data, list):
                    evidence_count += len(data)
            except Exception:
                pass

    # Schemas
    schemas_count = 0
    schemas_path = ROOT_DIR / "Hippocampus" / "Patterns" / "schemas.json"
    if schemas_path.exists():
        try:
            sd = json.loads(schemas_path.read_text(encoding="utf-8"))
            schemas_count = len(sd.get("schemas", [])) if isinstance(sd, dict) else 0
        except Exception:
            pass

    # Procedures
    proc_count = 0
    proc_path = EVIDENCE_DIR / "Metrics" / "procedural_log.json"
    if proc_path.exists():
        try:
            pd_data = json.loads(proc_path.read_text(encoding="utf-8"))
            proc_count = len(pd_data.get("procedures", [])) if isinstance(pd_data, dict) else 0
        except Exception:
            pass

    # Knowledge graph (SQLite — primary source of truth)
    graph_nodes = graph_edges = 0
    try:
        from pulse.core.brain_db import get_conn
        gconn = get_conn()
        graph_nodes = gconn.execute("SELECT COUNT(*) as c FROM graph_nodes").fetchone()["c"]
        graph_edges = gconn.execute("SELECT COUNT(*) as c FROM graph_edges").fetchone()["c"]
    except Exception:
        pass

    total_knowledge = brain_stats.get("lessons", 0) + brain_stats.get("failures", 0) + brain_stats.get("patterns", 0)
    extraction_rate = (total_knowledge / evidence_count * 100) if evidence_count > 0 else 0.0

    return {
        "orchestrator": {
            "running": orch_running,
            "pid": orch_pid,
        },
        "daemons": {
            "alive": daemons_alive,
            "dead": daemons_dead,
            "total": len(daemon_pids),
        },
        "workers": {
            "alive": workers_alive,
            "dead": workers_dead,
            "total": len(worker_records),
            "crew": [
                {
                    "id": r.get("id", "?"),
                    "model": r.get("model", "?"),
                    "tier": r.get("tier", "?"),
                    "mode": r.get("mode", "headless"),
                    "running": _is_process_running(r.get("pid", 0)),
                }
                for r in worker_records
            ],
        },
        "task_board": {
            "open": tasks_open,
            "claimed": tasks_claimed,
            "active": tasks_active,
            "done": tasks_done,
            "failed": tasks_failed,
            "total": tasks_total,
        },
        "brain": {
            "lessons": brain_stats.get("lessons", 0),
            "failures": brain_stats.get("failures", 0),
            "patterns": brain_stats.get("patterns", 0),
            "schemas": schemas_count,
            "procedures": proc_count,
            "evidence_items": evidence_count,
            "extraction_rate": round(extraction_rate, 2),
            "graph_nodes": graph_nodes,
            "graph_edges": graph_edges,
        },
    }
