from __future__ import annotations

import json
import os
import tempfile
import threading
from pathlib import Path
from typing import Any


class PersistentKeyValueFile:
    """Best-effort persistent key-value store backed by JSON."""

    def __init__(
        self,
        path: str | Path,
        *,
        file_mode: int | None = None,
        dir_mode: int | None = None,
    ):
        self.path = Path(path)
        self.file_mode = file_mode
        self.dir_mode = dir_mode
        self._lock = threading.Lock()
        self._mem: dict[str, Any] = {}
        self._loaded = False

    def _load_once(self) -> None:
        if self._loaded:
            return
        try:
            if not self.path.exists():
                self._mem = {}
                self._loaded = True
                return
            self._restrict_permissions(self.path, self.file_mode)
            raw = self.path.read_text(encoding="utf-8") or ""
            parsed = json.loads(raw) if raw.strip() else {}
            self._mem = parsed if isinstance(parsed, dict) else {}
            self._loaded = True
        except Exception:
            self._mem = {}
            self._loaded = True

    def get(self, key: str, default: Any = None) -> Any:
        with self._lock:
            self._load_once()
            return self._mem.get(str(key), default)

    def set(self, key: str, value: Any) -> None:
        with self._lock:
            self._load_once()
            self._mem[str(key)] = value
            self._persist()

    def delete(self, key: str) -> None:
        with self._lock:
            self._load_once()
            if str(key) in self._mem:
                del self._mem[str(key)]
                self._persist()

    def _atomic_write_text(self, data: str) -> None:
        with tempfile.NamedTemporaryFile("w", encoding="utf-8", dir=str(self.path.parent), delete=False) as f:
            tmp = Path(f.name)
            self._restrict_permissions(tmp, self.file_mode)
            f.write(data)
            f.flush()
            try:
                os.fsync(f.fileno())
            except Exception:
                pass
        try:
            tmp.replace(self.path)
            self._restrict_permissions(self.path, self.file_mode)
        except Exception:
            try:
                tmp.unlink(missing_ok=True)
            except Exception:
                pass
            raise

    def _persist(self) -> None:
        try:
            try:
                self.path.parent.mkdir(parents=True, exist_ok=True)
                self._restrict_permissions(self.path.parent, self.dir_mode)
            except Exception:
                pass
            data = json.dumps(self._mem, indent=2, sort_keys=True, default=str) + "\n"
            self._atomic_write_text(data)
        except Exception:
            return

    def _restrict_permissions(self, path: Path, mode: int | None) -> None:
        if mode is None or os.name != "posix":
            return
        try:
            path.chmod(mode)
        except Exception:
            return


__all__ = ["PersistentKeyValueFile"]
