#!/usr/bin/env python3
"""infra -- singura poarta prin care puntea Discord atinge infrastructura.

    infra <host> <comanda...>      ruleaza comanda pe hostul din lista
    infra --list                   arata hosturile permise
    infra --help

Hosturile sunt o lista EXPLICITA. Un host care nu e in lista este refuzat, fara
incercare de rezolvare DNS. Fiecare apel este jurnalizat in
~/.claude-discord/logs/infra.log cu data, host, comanda completa si rezultat.

Lista implicita poate fi inlocuita cu ~/.claude-discord/infra-hosts.json:

    {"pvemini": {"addr": "10.0.20.201", "user": "root", "prod": true,
                 "desc": "nod Proxmox principal"}}

Coduri de iesire proprii wrapper-ului (comenzile remote isi pastreaza codul lor):
    2  utilizare gresita (lipseste hostul sau comanda)
    3  host in afara listei
    4  eroare de configurare (fisier de hosturi corupt)
"""

from __future__ import annotations

import datetime
import json
import os
import pathlib
import shlex
import subprocess
import sys
import time

EXIT_USAGE = 2
EXIT_UNKNOWN_HOST = 3
EXIT_CONFIG = 4

# Lista implicita, din tabelul de retea al repo-ului (CLAUDE.md, proxmox/README.md).
DEFAULT_HOSTS: dict[str, dict] = {
    # noduri Proxmox
    "pve1":       {"addr": "10.0.20.200", "user": "root", "prod": True,  "desc": "nod Proxmox pve1"},
    "pvemini":    {"addr": "10.0.20.201", "user": "root", "prod": True,  "desc": "nod Proxmox principal"},
    "pveelite":   {"addr": "10.0.20.202", "user": "root", "prod": True,  "desc": "nod Proxmox pveelite"},
    # servicii interne
    "oracle":     {"addr": "10.0.20.121", "user": "root", "prod": False, "desc": "LXC 108 Oracle XE 21c/18c"},
    "flowise":    {"addr": "10.0.20.161", "user": "root", "prod": False, "desc": "LXC 104 Flowise"},
    "gitea":      {"addr": "10.0.20.165", "user": "root", "prod": False, "desc": "LXC Gitea"},
    "docker":     {"addr": "10.0.20.113", "user": "root", "prod": False, "desc": "LXC 102 Docker + Portainer"},
    "moltbot":    {"addr": "10.0.20.173", "user": "root", "prod": False, "desc": "LXC 110 MoltBot"},
    "dokploy":    {"addr": "10.0.20.167", "user": "root", "prod": False, "desc": "LXC 103 Dokploy"},
    # productie / clienti
    "oracle-prod": {"addr": "10.0.20.36", "user": "romfast", "prod": True, "desc": "server Oracle de PRODUCTIE"},
    "oracle-dr":   {"addr": "10.0.20.37", "user": "romfast", "prod": True, "desc": "server Oracle DR"},
    "roacentral":  {"addr": "10.0.20.122", "user": "romfast", "prod": True, "desc": "VM 201 Windows, IIS reverse proxy"},
    "oracle-test": {"addr": "10.0.20.130", "user": "romfast", "prod": False, "desc": "VM 302 mediu de test"},
}

SSH_OPTS = [
    "-o", "BatchMode=yes",
    "-o", "StrictHostKeyChecking=accept-new",
    "-o", "ConnectTimeout=10",
]


def state_dir() -> pathlib.Path:
    override = os.environ.get("CLAUDE_DISCORD_DIR")
    if override:
        return pathlib.Path(override)
    return pathlib.Path.home() / ".claude-discord"


def hosts_file() -> pathlib.Path:
    return state_dir() / "infra-hosts.json"


def log_file() -> pathlib.Path:
    return state_dir() / "logs" / "infra.log"


def load_hosts() -> dict[str, dict]:
    """Lista de hosturi: fisierul de pe disc daca exista, altfel cea implicita."""
    path = hosts_file()
    if not path.is_file():
        return dict(DEFAULT_HOSTS)
    try:
        data = json.loads(path.read_text(encoding="utf-8"))
    except (OSError, ValueError) as exc:
        raise SystemExit(_fail(EXIT_CONFIG, f"infra: {path} nu e JSON valid ({exc})"))
    if not isinstance(data, dict) or not data:
        raise SystemExit(_fail(EXIT_CONFIG, f"infra: {path} nu contine hosturi"))
    out: dict[str, dict] = {}
    for name, spec in data.items():
        if isinstance(spec, str):
            spec = {"addr": spec}
        if not isinstance(spec, dict) or not spec.get("addr"):
            raise SystemExit(_fail(EXIT_CONFIG, f"infra: intrare invalida pentru '{name}'"))
        out[str(name)] = {
            "addr": str(spec["addr"]),
            "user": str(spec.get("user") or "root"),
            "prod": bool(spec.get("prod", False)),
            "desc": str(spec.get("desc") or ""),
        }
    return out


def _fail(code: int, msg: str) -> int:
    sys.stderr.write(msg + "\n")
    return code


def log(host: str, target: str, cmd: list[str], rc, dur_s: float, note: str = "") -> None:
    """Jurnal pe o linie: data, host, comanda completa, rezultat."""
    line = (
        f"{datetime.datetime.now().isoformat(timespec='seconds')}\t"
        f"host={host}\ttarget={target}\trc={rc}\tdur={dur_s:.2f}s\t"
        f"cmd={shlex.join(cmd) if cmd else ''}"
    )
    if note:
        line += f"\tnote={note}"
    try:
        path = log_file()
        path.parent.mkdir(parents=True, exist_ok=True)
        with open(path, "a", encoding="utf-8") as fh:
            fh.write(line + "\n")
    except OSError:
        pass  # un jurnal care nu se poate scrie nu opreste comanda


def usage(hosts: dict[str, dict]) -> str:
    lines = ["infra <host> <comanda...>", "", "Hosturi permise:"]
    for name, spec in sorted(hosts.items()):
        flag = " [PRODUCTIE]" if spec.get("prod") else ""
        lines.append(f"  {name:<13} {spec['user']}@{spec['addr']:<13} {spec.get('desc','')}{flag}")
    lines.append("")
    lines.append(f"Lista se poate inlocui prin {hosts_file()}")
    return "\n".join(lines)


def main(argv: list[str]) -> int:
    hosts = load_hosts()

    if not argv or argv[0] in ("-h", "--help"):
        print(usage(hosts))
        return 0 if argv else EXIT_USAGE
    if argv[0] in ("-l", "--list"):
        print(usage(hosts))
        return 0

    host = argv[0]
    cmd = argv[1:]

    if host not in hosts:
        log(host, "-", cmd, "refuzat", 0.0, note="host in afara listei")
        return _fail(
            EXIT_UNKNOWN_HOST,
            f"infra: host necunoscut '{host}'. Hosturi permise: "
            + ", ".join(sorted(hosts)),
        )
    if not cmd:
        return _fail(EXIT_USAGE, f"infra: lipseste comanda pentru '{host}'")

    spec = hosts[host]
    target = f"{spec['user']}@{spec['addr']}"
    ssh_argv = ["ssh", *SSH_OPTS, target, "--", *cmd]

    if os.environ.get("INFRA_DRY_RUN"):
        log(host, target, cmd, "dry-run", 0.0, note="INFRA_DRY_RUN")
        print(shlex.join(ssh_argv))
        return 0

    t0 = time.monotonic()
    try:
        proc = subprocess.run(ssh_argv)
        rc = proc.returncode
    except FileNotFoundError:
        log(host, target, cmd, "eroare", time.monotonic() - t0, note="ssh lipseste")
        return _fail(EXIT_CONFIG, "infra: `ssh` nu exista in PATH")
    except KeyboardInterrupt:
        log(host, target, cmd, "intrerupt", time.monotonic() - t0)
        return 130
    log(host, target, cmd, rc, time.monotonic() - t0)
    return rc


if __name__ == "__main__":
    try:
        sys.exit(main(sys.argv[1:]))
    except SystemExit:
        raise
    except Exception as exc:  # nimic nu iese neraportat
        sys.stderr.write(f"infra: eroare interna: {exc}\n")
        sys.exit(1)
