#!/usr/bin/env python3
"""
hermes-doc-enrich — Hermes inspecte le cluster (LECTURE SEULE) et ajoute
une section "## État vérifié" à chaque doc admin/.
Jamais de suppression ni de réécriture — ajout uniquement.
Commit local dans hermes/daily-work + rag-ingest Qdrant à la fin.
"""

import argparse
import json
import subprocess
from datetime import datetime
from pathlib import Path

REPO_DIR   = Path("/srv/data/lab/Funk-lab")
BRANCH     = "hermes/daily-work"
ADMIN_DIR  = REPO_DIR / "admin"
RAG_DOCS   = Path("/srv/data/rag/docs")
STATE_FILE = Path("/var/lib/hermes-auto-improve/enrich-state.json")
MAX_FILES  = 3
SECTION_MARKER = "## État vérifié"

# ── Commandes read-only par fichier (clé = substring du chemin relatif) ──────
CONTEXT_MAP = {
    "ia/litellm": [
        "systemctl status litellm --no-pager -n 5",
        "cat /etc/litellm/config.yaml",
    ],
    "ia/hermes.md": [
        "systemctl status hermes --no-pager -n 5",
        "cat /srv/data/hermes/config.yaml 2>/dev/null | head -40",
    ],
    "ia/hermes-souls": [
        "ls /srv/data/hermes/ 2>/dev/null",
        "cat /srv/data/hermes/config.yaml 2>/dev/null | grep -A3 'profile' | head -20",
    ],
    "ia/hermes-voice": [
        "systemctl status hermes-voice 2>/dev/null --no-pager -n 5 || echo 'service hermes-voice absent'",
    ],
    "ia/llama_server": [
        "systemctl cat llama-server 2>/dev/null | grep -E 'ExecStart|Environment|Restart' | head -10",
        "ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 gpu-01 'systemctl status llama-server --no-pager -n 5' 2>/dev/null || echo 'gpu-01 inaccessible'",
    ],
    "ia/rag": [
        "systemctl status qdrant --no-pager -n 5",
        "ls /srv/data/rag/docs/ 2>/dev/null | wc -l",
        "/usr/local/bin/rag-query 'hermes' 2>&1 | head -10",
    ],
    "ia/rocm": [
        "ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 gpu-01 'uname -r; rocm-smi 2>/dev/null | head -10' 2>/dev/null || echo 'gpu-01 inaccessible'",
    ],
    "ia/alertmanager-webhook": [
        "systemctl status alertmanager-webhook --no-pager -n 5",
        "cat /etc/alertmanager-webhook/*.yaml 2>/dev/null | head -30",
    ],
    "infra/dnsmasq": [
        "systemctl status dnsmasq --no-pager -n 5",
        "dig @127.0.0.1 compute-01.lab.local +short 2>/dev/null",
        "dig @127.0.0.1 openwebui.lab.local +short 2>/dev/null",
    ],
    "infra/nfs": [
        "systemctl status nfs-server --no-pager -n 5",
        "exportfs -v 2>/dev/null",
    ],
    "infra/reseau": [
        "ip addr show | grep 'inet '",
        "nft list ruleset 2>/dev/null | grep -E 'chain|policy|iif|oif|dport' | head -30",
    ],
    "infra/ssh": [
        "sshd -T 2>/dev/null | grep -E 'permitrootlogin|passwordauth|pubkeyauth|^port '",
    ],
    "infra/email": [
        "systemctl status postfix --no-pager -n 5",
        "postconf -n 2>/dev/null | grep -vi 'pass' | grep -E 'relay|tls|sasl'",
    ],
    "install/storage-01": [
        "hostnamectl | grep -E 'Hostname|OS|Kernel'",
        "df -h | grep -v tmpfs",
        "free -h",
        "systemctl list-units --state=failed --no-pager 2>/dev/null | grep -v '^$'",
    ],
    "install/gpu-01": [
        "ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 gpu-01 'hostnamectl | grep -E \"Hostname|OS|Kernel\"; df -h | grep -v tmpfs; systemctl list-units --state=failed --no-pager 2>/dev/null' 2>/dev/null || echo 'gpu-01 inaccessible'",
    ],
    "install/kubernetes": [
        "kubectl get nodes -o wide 2>/dev/null",
        "kubectl get pods -A --field-selector=status.phase!=Running 2>/dev/null | head -10",
    ],
    "k8s/argocd": [
        "kubectl get applications -n argocd -o wide 2>/dev/null",
        "kubectl get pods -n argocd 2>/dev/null",
    ],
    "k8s/monitoring": [
        "kubectl get pods -n monitoring 2>/dev/null",
        "kubectl get ingress -n monitoring 2>/dev/null",
    ],
    "k8s/n8n": [
        "kubectl get all -n ai -l app=n8n 2>/dev/null",
        "kubectl get ingress -n ai 2>/dev/null",
    ],
    "k8s/open-webui": [
        "kubectl get all -n ai 2>/dev/null",
        "kubectl get ingress -n ai 2>/dev/null",
    ],
    "k8s/talos": [
        "kubectl get nodes 2>/dev/null",
        "talosctl version 2>/dev/null | head -4",
    ],
    "k8s/k9s": [
        "k9s version 2>/dev/null || echo 'k9s non installé sur storage-01'",
        "kubectl config get-contexts 2>/dev/null",
    ],
    "ops/ansible": [
        "ansible --version 2>/dev/null | head -3",
        "ls /srv/data/lab/Funk-lab/ansible/roles/",
    ],
    "ops/cluster": [
        "kubectl get nodes -o wide 2>/dev/null",
        "systemctl list-units --state=failed --no-pager 2>/dev/null | grep -v '^$' | head -10",
    ],
    "ops/systeme": [
        "uname -r",
        "uptime",
        "df -h | grep -v tmpfs",
        "free -h",
        "systemctl list-units --state=failed --no-pager 2>/dev/null | grep -v '^$' | head -10",
    ],
    "README": [
        "systemctl is-active litellm hermes dnsmasq nfs-server qdrant postfix 2>/dev/null",
    ],
}

DEFAULT_COMMANDS = [
    "date",
    "systemctl list-units --state=failed --no-pager 2>/dev/null | grep -v '^$' | head -5",
]


# ── Git ───────────────────────────────────────────────────────────────────────

def git(args):
    return subprocess.run(
        ["git", "-C", str(REPO_DIR)] + args,
        capture_output=True, text=True
    )


def git_sync():
    git(["config", "user.email", "hermes@funk.lab"])
    git(["config", "user.name",  "Hermes Bot"])
    git(["fetch", "origin"])
    r = git(["checkout", BRANCH])
    if r.returncode != 0:
        git(["checkout", "-b", BRANCH, f"origin/{BRANCH}"])
    git(["pull", "--rebase", "origin", BRANCH])


# ── Cluster inspection (read-only) ───────────────────────────────────────────

def run_cmd(cmd):
    try:
        r = subprocess.run(
            cmd, shell=True, capture_output=True, text=True, timeout=30
        )
        out = (r.stdout + r.stderr).strip()
        return out[:1500] if out else "(aucune sortie)"
    except subprocess.TimeoutExpired:
        return "(timeout)"
    except Exception as e:
        return f"(erreur: {e})"


def get_context_cmds(rel_path):
    for key, cmds in CONTEXT_MAP.items():
        if key in rel_path:
            return cmds
    return DEFAULT_COMMANDS


def collect_cluster_state(cmds):
    parts = []
    for cmd in cmds:
        out = run_cmd(cmd)
        parts.append(f"$ {cmd}\n{out}")
    return "\n\n".join(parts)


# ── Hermes ────────────────────────────────────────────────────────────────────

def ask_hermes(prompt):
    safe = prompt.replace("'", "'\\''")
    cmd  = [
        "sudo", "-i", "-u", "hermes", "bash", "-c",
        f"HERMES_HOME=/srv/data/hermes hermes --profile funk-ai -z '{safe}'"
    ]
    r = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
    return r.stdout.strip()


def build_verified_section(fpath, cluster_state, today):
    rel     = str(fpath.relative_to(REPO_DIR))
    content = fpath.read_text(encoding="utf-8", errors="replace")

    prompt = f"""Tu es l'agent de documentation du homelab Funk.
MISSION : écrire UNIQUEMENT une nouvelle section à ajouter à la fin de ce document.
CONTRAINTE ABSOLUE : ne touche pas au contenu existant — ajout uniquement.

Fichier concerné : {rel}
--- ÉTAT RÉEL DU CLUSTER (commandes read-only) ---
{cluster_state[:3000]}
--- FIN ---

Écris une section Markdown intitulée exactement :
## État vérifié — {today}

Cette section doit contenir :
- État des services concernés (actif/inactif/dégradé)
- Versions observées si pertinent
- Toute divergence notable entre la doc et l'état réel
- Tout élément opérationnel utile observé (ex: pods crashés, export NFS manquant, config inattendue)

RÈGLES :
- Sois concis (5-15 lignes max)
- Ne reproduis pas ce qui est déjà dans le document
- Si tout est conforme, dis-le en une phrase
- Réponds UNIQUEMENT avec le contenu Markdown de la section, rien d'autre"""

    result = ask_hermes(prompt)

    # S'assure que la section commence bien par le bon titre
    if SECTION_MARKER not in result:
        result = f"{SECTION_MARKER} — {today}\n\n{result}"

    return result, content


def enrich_file(fpath, today):
    rel = str(fpath.relative_to(REPO_DIR))

    cmds = get_context_cmds(rel)
    print(f"  Collecte état cluster ({len(cmds)} cmd)...", flush=True)
    cluster_state = collect_cluster_state(cmds)

    print(f"  Hermes génère la section...", flush=True)
    new_section, original_content = build_verified_section(fpath, cluster_state, today)

    if not new_section or len(new_section) < 30:
        return False, f"section trop courte ({len(new_section)} chars)"

    # Retire l'ancienne section "État vérifié" si elle existe déjà
    if SECTION_MARKER in original_content:
        # Coupe au premier SECTION_MARKER et remplace par la nouvelle section
        idx = original_content.index(SECTION_MARKER)
        base = original_content[:idx].rstrip()
    else:
        base = original_content.rstrip()

    updated = base + "\n\n---\n\n" + new_section.strip() + "\n"
    fpath.write_text(updated, encoding="utf-8")
    return True, None


# ── Qdrant re-ingest ──────────────────────────────────────────────────────────

def rag_ingest():
    subprocess.run(
        ["rsync", "-a", "--delete", str(ADMIN_DIR) + "/", str(RAG_DOCS) + "/"],
        check=True
    )
    r = subprocess.run(
        ["/usr/local/bin/rag-ingest"],
        capture_output=True, text=True, timeout=300
    )
    return (r.stdout + r.stderr).strip()[-300:]


# ── State ─────────────────────────────────────────────────────────────────────

def load_state():
    if STATE_FILE.exists():
        return json.loads(STATE_FILE.read_text())
    return {"enriched": [], "errors": []}


def save_state(state):
    STATE_FILE.parent.mkdir(parents=True, exist_ok=True)
    STATE_FILE.write_text(json.dumps(state, indent=2, ensure_ascii=False))


# ── Main ──────────────────────────────────────────────────────────────────────

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--all",     action="store_true", help="Traiter tous les fichiers en une passe")
    parser.add_argument("--reset",   action="store_true", help="Réinitialiser l'état de progression")
    parser.add_argument("--dry-run", action="store_true", help="Afficher le plan sans modifier les fichiers")
    parser.add_argument("--subdir",  default=None,        help="Sous-dossier admin/ (ex: ia, k8s)")
    parser.add_argument("--no-rag",  action="store_true", help="Ne pas relancer rag-ingest à la fin")
    args = parser.parse_args()

    today = datetime.now().strftime("%Y-%m-%d")
    now   = datetime.now().strftime("%Y-%m-%d_%H-%M")

    print(f"hermes-doc-enrich — {now}", flush=True)
    print(f"Branche : {BRANCH} (local, pas de push)", flush=True)

    if not args.dry_run:
        git_sync()

    state = load_state()
    if args.reset:
        state = {"enriched": [], "errors": []}
        save_state(state)
        print("État réinitialisé.\n")

    scan_root = ADMIN_DIR / args.subdir if args.subdir else ADMIN_DIR
    all_md = [
        f for f in sorted(scan_root.rglob("*.md"))
        if "hermes/builtin" not in str(f)
    ]

    if args.all:
        batch = all_md
    else:
        todo = [f for f in all_md if str(f) not in state["enriched"]]
        if not todo:
            state["enriched"] = []
            save_state(state)
            todo = all_md
            print("Rotation complète — tous les fichiers repassés.")
        batch = todo[:MAX_FILES]

    print(f"\n{len(batch)} fichiers à enrichir\n{'─'*50}", flush=True)

    changed = []
    for fpath in batch:
        rel = str(fpath.relative_to(REPO_DIR))
        print(f"\n→ {rel}", flush=True)

        if args.dry_run:
            cmds = get_context_cmds(rel)
            print(f"  [dry-run] {len(cmds)} commandes :")
            for c in cmds:
                print(f"    {c}")
            continue

        ok, err = enrich_file(fpath, today)
        if not ok:
            print(f"  ✗ {err}", flush=True)
            state["errors"].append(rel)
            continue

        state["enriched"].append(str(fpath))
        changed.append(rel)
        print(f"  ✓ Section ajoutée", flush=True)

        git(["add", rel])
        if git(["diff", "--cached", "--quiet"]).returncode != 0:
            git(["commit", "-m",
                 f"docs(hermes): état vérifié {Path(rel).name} — {today}\n\n"
                 f"Section '## État vérifié' ajoutée/mise à jour.\n"
                 f"Inspection read-only du cluster, aucune modification de service.\n\n"
                 "Co-Authored-By: Hermes <hermes@funk.lab>"])
            print(f"  ✓ Commit local", flush=True)
        else:
            print(f"  — Aucun changement détecté (section déjà à jour)", flush=True)

    if not args.dry_run:
        save_state(state)

    # ── Qdrant re-ingest ──────────────────────────────────────────────────────
    if not args.dry_run and changed and not args.no_rag:
        print(f"\n⏳ Qdrant re-ingest ({len(changed)} docs mis à jour)...", flush=True)
        try:
            out = rag_ingest()
            print(f"  ✓ {out}", flush=True)
        except Exception as e:
            print(f"  ✗ rag-ingest échoué : {e}", flush=True)

    print(f"\n{'─'*50}")
    print(f"Enrichissements : {len(changed)}/{len(batch)}")
    if changed:
        print("Fichiers mis à jour :")
        for f in changed:
            print(f"  • {f}")
    print(f"\nBranche : {BRANCH} (local, pas de push)")
    print("Pour créer la PR : hermes-auto-improve --daily-pr")


if __name__ == "__main__":
    main()
