from __future__ import annotations

import json
from collections.abc import Iterable
from pathlib import Path


class FactoryDecisionNotificationStore:
    """Edge-triggered seen-set: one desktop notification per decision id, ever.

    Persisted so tray restarts cannot re-notify. Pruning to the currently
    pending set is safe because a decision id never returns to pending —
    re-asks mint new ids.
    """

    def __init__(self, path: Path) -> None:
        self._path = path

    def record_notified(self, decision_id: str) -> bool:
        """True only the first time this decision id is seen."""
        state = self._read()
        if decision_id in state:
            return False
        state.add(decision_id)
        self._write(state)
        return True

    def prune(self, keep_ids: Iterable[str]) -> None:
        keep = set(keep_ids)
        state = self._read()
        pruned = state & keep
        if pruned != state:
            self._write(pruned)

    def _read(self) -> set[str]:
        if not self._path.exists():
            return set()
        try:
            data = json.loads(self._path.read_text(encoding="utf-8"))
        except (OSError, json.JSONDecodeError):
            return set()
        if not isinstance(data, list):
            return set()
        return {str(entry) for entry in data}

    def _write(self, state: set[str]) -> None:
        self._path.parent.mkdir(parents=True, exist_ok=True)
        temporary = self._path.with_suffix(f"{self._path.suffix}.tmp")
        temporary.write_text(json.dumps(sorted(state)) + "\n", encoding="utf-8")
        temporary.replace(self._path)
