"""
echo-praesentation-helper.py

Komfort-Wrapper für den Echo-Modus des neue-praesentation-Skills.
Drei Aktionen: backup, diff-report, rollback.

Aufruf-Beispiele:
    python echo-praesentation-helper.py --bundle <pfad> --action backup
    python echo-praesentation-helper.py --bundle <pfad> --action diff-report --transkript <pfad> --edits <json-pfad>
    python echo-praesentation-helper.py --bundle <pfad> --action rollback [--backup <zip-name>]
    python echo-praesentation-helper.py --bundle <pfad> --action list-backups

Backup-Konvention: <bundle>/_echo-backups/echo-backup-YYYY-MM-DD-HHMM.zip
Rolling: max. 5 Backups, älteste werden bei Überlauf gelöscht.

Diff-Report-Format: 5–10 Zeilen Markdown nach SKILL.md-Konvention.
"""

# --- Bundle-Pflicht-Sätze §1 (Wave I v0.17.0, Phase A) ---
# Pfad-Resolver-Standard (analog _cross_skill_evals/run_linkedin_wave_h_evals.py):
#   BUNDLE_ROOT = Path(__file__).resolve().parent.parent.parent.parent
#     # .skills/neue-praesentation/scripts/file.py -> Repo-Root
#   SKILL_ROOT = BUNDLE_ROOT / ".skills" / "neue-praesentation"
#   FOUNDATION = BUNDLE_ROOT / "_lib" / "designsystem" / "templates" / "praesentation"
# Echte Umlaute (ä ö ü ß) durchgehend, keine ae/oe/ue/ss-Substitutionen.

from __future__ import annotations

import argparse
import datetime as _dt
import json
import os
import shutil
import sys
import zipfile
from pathlib import Path

MAX_BACKUPS = 5
RUNTIME_DIRNAME = "_runtime"
BACKUP_DIRNAME = "_echo-backups"


def _now_stamp() -> str:
    return _dt.datetime.now().strftime("%Y-%m-%d-%H%M")


def _backup_dir(bundle: Path) -> Path:
    p = bundle / BACKUP_DIRNAME
    p.mkdir(parents=True, exist_ok=True)
    return p


def _runtime_dir(bundle: Path) -> Path:
    p = bundle / RUNTIME_DIRNAME
    if not p.is_dir():
        sys.exit(f"FEHLER: kein _runtime/-Ordner unter {bundle}")
    return p


def action_backup(bundle: Path, prefix: str = "echo") -> Path:
    """Erstellt ein ZIP-Backup von _runtime/, rolling auf MAX_BACKUPS."""
    runtime = _runtime_dir(bundle)
    backups = _backup_dir(bundle)
    target = backups / f"{prefix}-backup-{_now_stamp()}.zip"

    with zipfile.ZipFile(target, "w", zipfile.ZIP_DEFLATED) as z:
        for root, _, files in os.walk(runtime):
            for f in files:
                full = Path(root) / f
                arc = full.relative_to(bundle)
                z.write(full, arc)

    _rotate_backups(backups, prefix)
    return target


def _rotate_backups(backups: Path, prefix: str) -> None:
    """Behält nur die letzten MAX_BACKUPS Dateien mit dem Prefix."""
    pattern = f"{prefix}-backup-*.zip"
    files = sorted(backups.glob(pattern), key=lambda p: p.stat().st_mtime)
    overflow = max(0, len(files) - MAX_BACKUPS)
    for f in files[:overflow]:
        f.unlink()


def action_list_backups(bundle: Path) -> list[dict]:
    backups = _backup_dir(bundle)
    items = []
    for f in sorted(backups.glob("*.zip"), reverse=True):
        items.append(
            {
                "name": f.name,
                "size_kb": round(f.stat().st_size / 1024, 1),
                "mtime": _dt.datetime.fromtimestamp(f.stat().st_mtime).isoformat(timespec="minutes"),
            }
        )
    return items


def action_rollback(bundle: Path, backup_name: str | None = None) -> Path:
    """Stellt _runtime/ aus einem Backup-ZIP wieder her.
    Wenn kein Name angegeben: das jüngste Backup.
    """
    backups = _backup_dir(bundle)
    if backup_name:
        zip_path = backups / backup_name
        if not zip_path.is_file():
            sys.exit(f"FEHLER: Backup nicht gefunden: {zip_path}")
    else:
        candidates = sorted(backups.glob("*.zip"), key=lambda p: p.stat().st_mtime, reverse=True)
        if not candidates:
            sys.exit(f"FEHLER: keine Backups in {backups}")
        zip_path = candidates[0]

    runtime = bundle / RUNTIME_DIRNAME
    if runtime.exists():
        shutil.rmtree(runtime)

    with zipfile.ZipFile(zip_path, "r") as z:
        z.extractall(bundle)

    return zip_path


def action_diff_report(bundle: Path, transkript: Path, edits_json: Path) -> Path:
    """Schreibt einen Diff-Report im SKILL.md-Format.

    edits-json-Schema:
    {
      "form": "Mehrsprecher-Dialog" | "Einsprecher" | "Stefan-Solo",
      "edits": [
        {"slide_id": "risiken-engpaesse", "kind": "in_place", "tag": "erweiterbar",
         "summary": "Bullet zur China-Ost-Route ergänzt", "konfidenz": 0.85}
      ],
      "inserted": [
        {"slide_id": "migration-asien-europa", "after": "risiken-engpaesse",
         "summary": "Migrations-Strategie", "konfidenz": 0.88}
      ],
      "unchanged": ["cover", "agenda", "ist-zustand", "empfehlungen", ...],
      "filtered": [
        {"thema": "Wochenende mit Kindern", "grund": "Privates"}
      ],
      "verworfen_relevant": [
        {"thema": "Marketing-Budget Q3", "grund": "außerhalb der Story", "konfidenz": 0.55}
      ],
      "backup_name": "echo-backup-2026-05-04-2101.zip"
    }
    """
    if not transkript.is_file():
        sys.exit(f"FEHLER: Transkript nicht gefunden: {transkript}")
    if not edits_json.is_file():
        sys.exit(f"FEHLER: Edits-JSON nicht gefunden: {edits_json}")

    transkript_size = transkript.stat().st_size
    data = json.loads(edits_json.read_text(encoding="utf-8"))

    stamp = _now_stamp()
    out = bundle / f"echo-diff-{stamp}.md"

    lines = []
    lines.append(f"# Echo-Diff — {_dt.datetime.now().strftime('%Y-%m-%d %H:%M')}")
    lines.append("")
    lines.append(
        f"Quelle: {transkript.name} ({transkript_size:,} Zeichen, Form: {data.get('form', 'unbekannt')})".replace(",", ".")
    )
    lines.append(f"Bundle: {bundle.as_posix()}/")
    lines.append("")

    edits = data.get("edits", []) + [
        {
            "slide_id": e["slide_id"],
            "kind": "insert",
            "after": e.get("after"),
            "before": e.get("before"),
            "summary": e["summary"],
            "konfidenz": e["konfidenz"],
        }
        for e in data.get("inserted", [])
    ]

    if not edits:
        lines.append("## Status: Keine Edits")
        lines.append("")
        lines.append(
            "Echo-Lauf abgeschlossen, Bundle unverändert. "
            "Alle Themen aus dem Transkript wurden ausgefiltert oder erreichten die Konfidenz-Schwelle nicht."
        )
        lines.append("")
    else:
        lines.append("## Geändert")
        lines.append("")
        for e in edits:
            kind_label = {
                "in_place": "erweitert",
                "replace": "ersetzt",
                "insert": "NEU",
                "agenda_sync": "Agenda synchronisiert",
            }.get(e["kind"], e["kind"])
            tag_part = f" ({e.get('tag', '')})" if e.get("tag") else ""
            pos = ""
            if e["kind"] == "insert":
                if e.get("after"):
                    pos = f" eingefügt nach `{e['after']}`"
                elif e.get("before"):
                    pos = f" eingefügt vor `{e['before']}`"
            konf = f" — Konfidenz {e['konfidenz']:.2f}"
            lines.append(f"- **{e['slide_id']}**{tag_part}: {kind_label}{pos} — {e['summary']}{konf}")
        lines.append("")

    if data.get("unchanged"):
        lines.append("## Unverändert")
        lines.append("")
        lines.append(", ".join(f"`{x}`" for x in data["unchanged"]))
        lines.append("")

    if data.get("filtered"):
        lines.append("## Verworfen (Filter)")
        lines.append("")
        for f in data["filtered"]:
            lines.append(f"- „{f['thema']}" + "“" + f" — {f['grund']}")
        lines.append("")

    if data.get("verworfen_relevant"):
        lines.append("## Verworfen, evtl. relevant")
        lines.append("")
        for f in data["verworfen_relevant"]:
            konf = f" (Konfidenz {f['konfidenz']:.2f})" if f.get("konfidenz") is not None else ""
            lines.append(f"- „{f['thema']}" + "“" + f" — {f['grund']}{konf}")
        lines.append("")

    if data.get("backup_name"):
        lines.append("## Backup")
        lines.append("")
        lines.append(f"`{BACKUP_DIRNAME}/{data['backup_name']}`")
        lines.append("")
        lines.append(
            f"Rollback: `python echo-praesentation-helper.py --bundle <Bundle> --action rollback --backup {data['backup_name']}`"
        )

    out.write_text("\n".join(lines), encoding="utf-8")
    return out


def main() -> None:
    ap = argparse.ArgumentParser(description="Echo-Helper für neue-praesentation-Skill.")
    ap.add_argument("--bundle", required=True, type=Path, help="Pfad zum Präsentations-Bundle (Ordner mit _runtime/)")
    ap.add_argument(
        "--action",
        required=True,
        choices=["backup", "diff-report", "rollback", "list-backups"],
        help="Welche Aktion ausführen",
    )
    ap.add_argument("--transkript", type=Path, help="Pfad zum Transkript (nur für diff-report)")
    ap.add_argument("--edits", type=Path, help="Pfad zur Edits-JSON-Datei (nur für diff-report)")
    ap.add_argument("--backup", type=str, help="Backup-Datei-Name (nur für rollback, optional)")
    ap.add_argument("--prefix", type=str, default="echo", help="Backup-Prefix (echo/update). Default: echo")
    args = ap.parse_args()

    bundle = args.bundle.resolve()
    if not bundle.is_dir():
        sys.exit(f"FEHLER: Bundle-Ordner nicht gefunden: {bundle}")

    if args.action == "backup":
        path = action_backup(bundle, prefix=args.prefix)
        print(f"BACKUP_OK {path}")
    elif args.action == "diff-report":
        if not args.transkript or not args.edits:
            sys.exit("FEHLER: --transkript und --edits sind Pflicht für diff-report")
        path = action_diff_report(bundle, args.transkript.resolve(), args.edits.resolve())
        print(f"DIFF_OK {path}")
        print(path.read_text(encoding="utf-8"))
    elif args.action == "rollback":
        path = action_rollback(bundle, backup_name=args.backup)
        print(f"ROLLBACK_OK {path}")
    elif args.action == "list-backups":
        items = action_list_backups(bundle)
        print(json.dumps(items, indent=2, ensure_ascii=False))


if __name__ == "__main__":
    main()
