"""Snapshot/Drift: speichere den Paket-Stand und vergleiche über Zeit.

Snapshots leben unter ~/.local/share/taninux/snapshots/<ts>.json mit:
  - timestamp
  - explicit: pacman -Qenq (explicit installiert, native repos)
  - aur:      pacman -Qmq  (foreign — meist AUR)
  - deps:     pacman -Qdq  (als dependency installiert)
"""
from __future__ import annotations

import json
import subprocess
from dataclasses import dataclass, field
from datetime import datetime
from pathlib import Path

from taninux.core.paths import snapshots_dir


@dataclass
class Snapshot:
    id: str                  # e.g. '20260526T195430'
    path: Path
    timestamp: str
    label: str
    explicit: set[str] = field(default_factory=set)
    aur: set[str] = field(default_factory=set)
    deps: set[str] = field(default_factory=set)

    @property
    def all_pkgs(self) -> set[str]:
        return self.explicit | self.aur | self.deps


def _pacman_qq(args: list[str]) -> set[str]:
    try:
        r = subprocess.run(
            ["pacman", *args],
            capture_output=True, text=True, check=False, timeout=10,
        )
    except (FileNotFoundError, subprocess.TimeoutExpired):
        return set()
    return {l.strip() for l in r.stdout.splitlines() if l.strip()}


def take_snapshot(label: str | None = None) -> Snapshot:
    ts = datetime.now().strftime("%Y%m%dT%H%M%S")
    iso = datetime.now().isoformat(timespec="seconds")
    snapshot_id = ts
    path = snapshots_dir() / f"{snapshot_id}.json"
    explicit = _pacman_qq(["-Qenq"])
    aur = _pacman_qq(["-Qmq"])
    deps = _pacman_qq(["-Qdq"])
    payload = {
        "id": snapshot_id,
        "label": label or "manual",
        "timestamp": iso,
        "explicit": sorted(explicit),
        "aur": sorted(aur),
        "deps": sorted(deps),
    }
    path.write_text(json.dumps(payload, indent=2))
    return Snapshot(
        id=snapshot_id,
        path=path,
        timestamp=iso,
        label=payload["label"],
        explicit=explicit,
        aur=aur,
        deps=deps,
    )


def list_snapshots() -> list[Snapshot]:
    out: list[Snapshot] = []
    for p in sorted(snapshots_dir().glob("*.json")):
        try:
            data = json.loads(p.read_text())
        except (OSError, json.JSONDecodeError):
            continue
        out.append(Snapshot(
            id=data.get("id", p.stem),
            path=p,
            timestamp=data.get("timestamp", ""),
            label=data.get("label", ""),
            explicit=set(data.get("explicit", [])),
            aur=set(data.get("aur", [])),
            deps=set(data.get("deps", [])),
        ))
    return out


def load(snapshot_id: str) -> Snapshot | None:
    for s in list_snapshots():
        if s.id == snapshot_id:
            return s
    return None


@dataclass
class Diff:
    added_explicit: list[str]
    removed_explicit: list[str]
    added_aur: list[str]
    removed_aur: list[str]
    added_deps: list[str]
    removed_deps: list[str]


def current_snapshot() -> Snapshot:
    return Snapshot(
        id="current",
        path=Path("/dev/null"),
        timestamp=datetime.now().isoformat(timespec="seconds"),
        label="now",
        explicit=_pacman_qq(["-Qenq"]),
        aur=_pacman_qq(["-Qmq"]),
        deps=_pacman_qq(["-Qdq"]),
    )


def diff(old: Snapshot, new: Snapshot) -> Diff:
    def sorted_list(s: set[str]) -> list[str]:
        return sorted(s)
    return Diff(
        added_explicit=sorted_list(new.explicit - old.explicit),
        removed_explicit=sorted_list(old.explicit - new.explicit),
        added_aur=sorted_list(new.aur - old.aur),
        removed_aur=sorted_list(old.aur - new.aur),
        added_deps=sorted_list(new.deps - old.deps),
        removed_deps=sorted_list(old.deps - new.deps),
    )


def latest() -> Snapshot | None:
    snaps = list_snapshots()
    return snaps[-1] if snaps else None
