#!/usr/bin/env python3
"""RSI cron + ZTE auto-patch. Detects degraded skills, snapshots, issues signed /deploy actions, rolls back on failure."""
import hashlib
import hmac
import json
import os
import shutil
import subprocess
import sys
import time
import urllib.error
import urllib.request
from pathlib import Path
from datetime import datetime

SKILLS_DIR = Path(os.environ.get("HERMES_HOME", "/opt/data")) / "skills"
SNAPSHOT_DIR = Path("/tmp/rsi-snapshots")
DEPLOY_URL = os.environ.get("DEPLOY_URL", "http://localhost:8099/deploy")
DEPLOY_TOKEN = os.environ.get("DEPLOY_TOKEN", "factory-deploy-token-2026")
FAIL_THRESHOLD = 0.8


def run(cmd, timeout=30):
    return subprocess.run(cmd, shell=isinstance(cmd, str), capture_output=True, text=True, timeout=timeout)


def signed_payload(action, target, actor="skill_health", payload_hash="none"):
    data = {
        "timestamp": int(time.time()),
        "nonce": hashlib.sha256(f"{time.time()}:{target}:{action}".encode()).hexdigest()[:16],
        "actor": actor,
        "action": action,
        "target": target,
        "payload_hash": payload_hash,
    }
    body = json.dumps({k: data[k] for k in ("timestamp", "nonce", "actor", "action", "target", "payload_hash")}, sort_keys=True, separators=(",", ":")).encode()
    data["signature"] = hmac.new(DEPLOY_TOKEN.encode(), body, hashlib.sha256).hexdigest()
    return data


def post_deploy(payload, timeout=15):
    req = urllib.request.Request(
        DEPLOY_URL,
        data=json.dumps(payload).encode(),
        headers={"Authorization": f"Bearer {DEPLOY_TOKEN}", "Content-Type": "application/json"},
        method="POST",
    )
    try:
        with urllib.request.urlopen(req, timeout=timeout) as r:
            return r.read().decode()
    except urllib.error.HTTPError as e:
        return e.read().decode()


def check_skill(name):
    d = SKILLS_DIR / name
    t = d / "test.sh"
    if not t.exists():
        return None
    r = run(["bash", str(t)])
    if r.returncode == 0:
        Path("/opt/data/rsi-known-good").mkdir(exist_ok=True)
        (Path("/opt/data/rsi-known-good") / f"{name}.sig").write_text(hashlib.sha256(open(t, "rb").read()).hexdigest()[:16])
    return {"skill": name, "passed": r.returncode == 0}


def snapshot():
    SNAPSHOT_DIR.mkdir(parents=True, exist_ok=True)
    tag = datetime.now().strftime("%Y%m%d_%H%M%S")
    shutil.copytree(SKILLS_DIR, SNAPSHOT_DIR / tag, dirs_exist_ok=True)
    (SNAPSHOT_DIR / "latest").write_text(tag)
    return tag


def rollback():
    lf = SNAPSHOT_DIR / "latest"
    if not lf.exists():
        return False
    snap = SNAPSHOT_DIR / lf.read_text().strip()
    if not snap.exists():
        return False
    shutil.rmtree(SKILLS_DIR)
    shutil.copytree(snap, SKILLS_DIR, dirs_exist_ok=True)
    return True


def auto_patch(failed):
    """ZTE: snapshot, request safe skill action, verify, rollback if still failing."""
    print(f"🔄 ZTE: auto-patching {len(failed)} degraded skills")
    snap = snapshot()
    print(f"📸 Snap: {snap}")
    for f in failed:
        skill = f["skill"]
        print(f"  Fixing {skill}...")
        payload_hash = hashlib.sha256(f"{snap}:{skill}".encode()).hexdigest()[:16]
        result = post_deploy(signed_payload("patch_skill_from_pr", skill, payload_hash=payload_hash))
        print(f"  Result: {result[:100]}")
    verify = [check_skill(f["skill"]) for f in failed]
    still_failing = [v for v in verify if v and not v["passed"]]
    if still_failing:
        print(f"⚠️ {len(still_failing)} still failing, rolling back...")
        rollback()
        print("✅ Rolled back")
    else:
        print("✅ All patched successfully")


def main():
    do_patch = "--auto-patch" in sys.argv
    results = [check_skill(d.name) for d in sorted(SKILLS_DIR.iterdir()) if d.is_dir() and (d / "test.sh").exists()]
    results = [r for r in results if r]
    passed = sum(1 for r in results if r["passed"])
    total = len(results)
    score = round(passed / total, 2) if total else 0
    failed = [r for r in results if not r["passed"]]
    for r in results:
        print(f"{'✅' if r['passed'] else '❌'} {r['skill']}")
    print(f"\nRSI: {passed}/{total} ({score:.0%})")
    if failed:
        print(f"Degraded: {[f['skill'] for f in failed]}")
        if do_patch:
            auto_patch(failed)
        else:
            print("Reported (no auto-patch)")
    else:
        print("All healthy — ZTE idle")


if __name__ == "__main__":
    main()
