from __future__ import annotations

import json
from pathlib import Path


class QuotaNotificationStore:
    """Edge-triggered per-window exhaustion state.

    reset_at values drift between polls (providers report relative reset
    seconds), so equality on them cannot deduplicate; only the
    exhausted/recovered transition can.
    """

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

    def record_warning(self, account_key: str, window: str) -> bool:
        """True only the first time a window crosses its warning threshold.

        Stays true until record_warning_reset sees usage back above the
        threshold, so restarts and polls that carry no reading cannot re-warn.
        """
        state = self._read()
        key = self._warning_key(account_key, window)
        if state.get(key, {}).get("warned") is True:
            return False
        state[key] = {"warned": True}
        self._write(state)
        return True

    def record_warning_reset(self, account_key: str, window: str) -> bool:
        state = self._read()
        key = self._warning_key(account_key, window)
        if state.get(key, {}).get("warned") is not True:
            return False
        state[key] = {"warned": False}
        self._write(state)
        return True

    def record_exhaustion(self, account_key: str, window: str, reset_at: str) -> bool:
        state = self._read()
        key = self._key(account_key, window)
        record = state.get(key, {})
        if self._is_exhausted(record):
            return False
        state[key] = {
            "exhausted": True,
            "exhausted_reset_at": reset_at,
            "reset_notified_at": record.get("reset_notified_at"),
        }
        self._write(state)
        return True

    def record_reset(self, account_key: str, window: str, reset_at: str) -> bool:
        state = self._read()
        key = self._key(account_key, window)
        record = state.get(key)
        if record is None or not self._is_exhausted(record):
            return False
        record["exhausted"] = False
        record["reset_notified_at"] = reset_at
        state[key] = record
        self._write(state)
        return True

    @staticmethod
    def _is_exhausted(record: dict[str, object]) -> bool:
        if "exhausted" in record:
            return record.get("exhausted") is True
        return record.get("exhausted_reset_at") is not None

    @staticmethod
    def _key(account_key: str, window: str) -> str:
        return f"{account_key}:{window}"

    @staticmethod
    def _warning_key(account_key: str, window: str) -> str:
        return f"warn:{account_key}:{window}"

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

    def _write(self, state: dict[str, dict[str, object]]) -> 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(state, indent=2, sort_keys=True) + "\n", encoding="utf-8")
        temporary.replace(self._path)
