"""
Hermes-Agent Control-Plane — read-only Einblicke fuer das Dashboard (Phase 4).

Macht das "geschenkte v9" sichtbar: Agent-Status, Cron-Scheduler und Skills des
Nous-Hermes-Agent-Frameworks. Quellen (MC laeuft als hitonabi auf dem Bosgame,
also direkter Zugriff):

  - Status  : ~/.hermes/gateway_state.json + cron-Heartbeat-Datei (sauberes JSON,
              kein Parsing). Liveness kommt aus dem Heartbeat, nicht aus dem evtl.
              beim Start eingefrorenen gateway_state.json.
  - Cron    : `hermes cron list --all` (kanonisch; kein stabiles Storage-Schema).
  - Skills  : `hermes skills list` (kanonisch) + ~/.hermes/skills/.usage.json
              (Nutzungszaehler). Rich-Tabellen werden mit COLUMNS=400 ohne
              Truncation erzeugt und ueber die `│`/`┃`-Spalten geparst.

Alles mit kurzem TTL-Cache, damit Dashboard-Polling die CLI nicht haemmert.
"""

import json
import os
import re
import subprocess
import time
from typing import Callable

from config import HERMES_HOME, HERMES_BIN

_CACHE_TTL = 10.0  # Sekunden
_cache: dict[str, tuple[float, object]] = {}


def _cached(key: str, fn: Callable[[], object]) -> object:
    now = time.monotonic()
    hit = _cache.get(key)
    if hit and now - hit[0] < _CACHE_TTL:
        return hit[1]
    val = fn()
    _cache[key] = (now, val)
    return val


def _run_cli(args: list[str], timeout: float = 45.0) -> str:
    """`hermes <args>` ausfuehren. COLUMNS=400 verhindert Rich-Truncation."""
    env = dict(os.environ, COLUMNS="400", NO_COLOR="1")
    try:
        p = subprocess.run(
            [HERMES_BIN, *args],
            capture_output=True, text=True, timeout=timeout, env=env,
        )
        return p.stdout or ""
    except Exception:
        return ""


def _parse_rich_table(text: str) -> list[dict]:
    """Eine Rich-Box-Tabelle in list[dict] verwandeln.

    Erste Zeile mit Spaltentrennern (`│` oder `┃`) ist der Header, der Rest sind
    Datenzeilen. Trenn-/Rahmenzeilen (┏━┓ usw.) enthalten keine Trenner -> ignoriert.
    """
    header: list[str] | None = None
    rows: list[list[str]] = []
    for ln in text.splitlines():
        if "│" not in ln and "┃" not in ln:
            continue
        cells = [c.strip() for c in re.split(r"[│┃]", ln)]
        # fuehrende/abschliessende Rahmen-Leerzellen entfernen
        while cells and cells[0] == "":
            cells.pop(0)
        while cells and cells[-1] == "":
            cells.pop()
        if not cells:
            continue
        if header is None:
            header = [c.lower() for c in cells]
        else:
            rows.append(cells)
    if not header:
        return []
    out = []
    for r in rows:
        if len(r) != len(header):
            continue
        out.append(dict(zip(header, r)))
    return out


# ---------------------------------------------------------------------------
# Oeffentliche Reads
# ---------------------------------------------------------------------------

def agent_status() -> dict:
    """Live-Status des Hermes-Gateways + Cron-Tickers."""
    def _load() -> dict:
        st = {
            "gateway_state": "unknown",
            "pid": None,
            "active_agents": None,
            "api_server": "unknown",
            "cron_running": False,
            "heartbeat_age_s": None,
            "version": None,
        }
        try:
            d = json.loads((HERMES_HOME / "gateway_state.json").read_text())
            st["gateway_state"] = d.get("gateway_state", "unknown")
            st["pid"] = d.get("pid")
            st["active_agents"] = d.get("active_agents")
            api = (d.get("platforms") or {}).get("api_server") or {}
            st["api_server"] = api.get("state", "unknown")
        except Exception:
            pass
        # Liveness: Cron-Ticker-Heartbeat (Unix-Timestamp). < 120s = laeuft.
        try:
            ts = float((HERMES_HOME / "cron" / "ticker_heartbeat").read_text().strip())
            age = max(0, int(time.time() - ts))
            st["heartbeat_age_s"] = age
            st["cron_running"] = age < 120
        except Exception:
            pass
        try:
            st["version"] = (HERMES_HOME / "PINNED_VERSION").read_text().strip() or None
        except Exception:
            pass
        return st

    return _cached("agent", _load)


def cron_jobs() -> list[dict]:
    """Geplante Jobs (inkl. pausierter)."""
    return _cached("cron", lambda: _parse_rich_table(_run_cli(["cron", "list", "--all"])))  # type: ignore[return-value]


def skills() -> list[dict]:
    """Installierte Skills, angereichert um Nutzungszaehler aus .usage.json."""
    def _load() -> list[dict]:
        rows = _parse_rich_table(_run_cli(["skills", "list"]))
        usage = {}
        try:
            usage = json.loads((HERMES_HOME / "skills" / ".usage.json").read_text())
        except Exception:
            pass
        for r in rows:
            u = usage.get(r.get("name", ""), {})
            r["use_count"] = u.get("use_count", 0)
            r["last_used_at"] = u.get("last_used_at")
            r["pinned"] = bool(u.get("pinned", False))
        return rows

    return _cached("skills", _load)  # type: ignore[return-value]
