"""Failure history — auto-save last 5 failed runs."""
from __future__ import annotations

import gzip
import json
from datetime import datetime, timezone
from pathlib import Path
from typing import Optional

FAILURE_DIR = Path.home() / ".agentradar" / "failures"
MAX_FAILURES = 5


def _ensure_dir():
    FAILURE_DIR.mkdir(parents=True, exist_ok=True)


def save_failure(run_state: dict) -> Path:
    """Auto-save a failed run. Evicts oldest if over limit."""
    _ensure_dir()
    run_id = run_state.get("run_id", "unknown")
    timestamp = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
    filename = f"failure_{run_id}_{timestamp}.agtrace"
    path = FAILURE_DIR / filename

    payload = {
        "version": "1.0.0",
        "created_at": datetime.now(timezone.utc).isoformat(),
        "pinned": False,
        "run": run_state,
    }
    data = json.dumps(payload, default=str).encode("utf-8")
    with gzip.open(str(path), "wb") as f:
        f.write(data)

    _evict_old_failures()
    return path


def _evict_old_failures():
    """Remove oldest unpinned failures if over MAX_FAILURES limit."""
    failures = _list_raw()
    unpinned = [f for f in failures if not f.get("pinned", False)]
    if len(failures) > MAX_FAILURES:
        # Sort by modified time, delete oldest unpinned
        unpinned_paths = sorted(
            [(f["path"], f["modified"]) for f in unpinned],
            key=lambda x: x[1],
        )
        to_delete = len(failures) - MAX_FAILURES
        for path, _ in unpinned_paths[:to_delete]:
            try:
                Path(path).unlink()
            except Exception:
                pass


def list_failures() -> list[dict]:
    """List all saved failure runs."""
    _ensure_dir()
    results = []
    for path in sorted(FAILURE_DIR.glob("*.agtrace"), key=lambda p: p.stat().st_mtime, reverse=True):
        try:
            with gzip.open(str(path), "rb") as f:
                data = json.loads(f.read().decode("utf-8"))
            run = data.get("run", {})
            agents = run.get("agents", {})
            failed_agents = [a.get("agent_name", "?") for a in agents.values() if a.get("status") == "failed"]
            results.append({
                "id": path.stem,
                "filename": path.name,
                "path": str(path),
                "run_id": run.get("run_id", ""),
                "created_at": data.get("created_at", ""),
                "pinned": data.get("pinned", False),
                "failed_agents": failed_agents,
                "modified": datetime.fromtimestamp(path.stat().st_mtime, tz=timezone.utc).isoformat(),
            })
        except Exception:
            pass
    return results


def _list_raw() -> list[dict]:
    results = []
    for path in FAILURE_DIR.glob("*.agtrace"):
        try:
            with gzip.open(str(path), "rb") as f:
                data = json.loads(f.read().decode("utf-8"))
            results.append({
                "path": str(path),
                "pinned": data.get("pinned", False),
                "modified": path.stat().st_mtime,
            })
        except Exception:
            pass
    return results


def load_failure(failure_id: str) -> dict:
    """Load a specific failure by its stem name."""
    path = FAILURE_DIR / f"{failure_id}.agtrace"
    if not path.exists():
        raise FileNotFoundError(f"Failure not found: {failure_id}")
    with gzip.open(str(path), "rb") as f:
        return json.loads(f.read().decode("utf-8"))


def pin_failure(failure_id: str) -> bool:
    """Pin a failure so it's never auto-evicted."""
    path = FAILURE_DIR / f"{failure_id}.agtrace"
    if not path.exists():
        return False
    with gzip.open(str(path), "rb") as f:
        data = json.loads(f.read().decode("utf-8"))
    data["pinned"] = True
    with gzip.open(str(path), "wb") as f:
        f.write(json.dumps(data).encode("utf-8"))
    return True


def clear_failures() -> int:
    """Delete all unpinned failure files."""
    _ensure_dir()
    count = 0
    for path in FAILURE_DIR.glob("*.agtrace"):
        try:
            with gzip.open(str(path), "rb") as f:
                data = json.loads(f.read().decode("utf-8"))
            if not data.get("pinned", False):
                path.unlink()
                count += 1
        except Exception:
            try:
                path.unlink()
                count += 1
            except Exception:
                pass
    return count
