"""Council spawn rate caps (daily + hourly) with durable counters."""

from __future__ import annotations

import logging
import os
import time
from datetime import datetime

from persistence import MUGI_DIR, secure_atomic_write

_log = logging.getLogger(__name__)


def _cap_state_path() -> str:
    return os.path.join(MUGI_DIR, "council_cap_state.json")


def _read_json_safe(path: str) -> dict:
    try:
        if not os.path.isfile(path):
            return {}
        import json

        with open(path, encoding="utf-8") as fh:
            data = json.load(fh)
        return data if isinstance(data, dict) else {}
    except Exception:
        _log.debug("council cap state read failed", exc_info=True)
        return {}


def _write_json_secure(path: str, data: dict) -> None:
    os.makedirs(os.path.dirname(path) or MUGI_DIR, exist_ok=True)
    import json

    real = os.path.realpath(path)
    expected_dir = os.path.realpath(MUGI_DIR)
    if not real.startswith(expected_dir + os.sep) and real != expected_dir:
        _log.error(
            "council cap state not persisted (path outside MUGI_DIR): %s — caps disabled until fixed",
            real,
        )
        return
    secure_atomic_write(path, json.dumps(data, indent=2, ensure_ascii=True))
    try:
        os.chmod(path, 0o600)
    except OSError:
        pass


def _local_day_key() -> str:
    return datetime.now().strftime("%Y-%m-%d")


def _local_hour_key() -> str:
    return datetime.now().strftime("%Y-%m-%dT%H")


def _load_state() -> dict:
    return _read_json_safe(_cap_state_path())


def _save_state(state: dict) -> None:
    _write_json_secure(_cap_state_path(), state)


def _normalize_state(state: dict) -> dict:
    day = _local_day_key()
    hour = _local_hour_key()
    if str(state.get("day") or "") != day:
        state["day"] = day
        state["daily_count"] = 0
    if str(state.get("hour_bucket") or "") != hour:
        state["hour_bucket"] = hour
        state["hourly_count"] = 0
    state["daily_count"] = max(0, int(state.get("daily_count", 0) or 0))
    state["hourly_count"] = max(0, int(state.get("hourly_count", 0) or 0))
    return state


def _cap_limits() -> tuple[int, int]:
    try:
        from core.config import get_council_config

        cfg = get_council_config()
    except Exception:
        cfg = {}
    def _cap_int(key: str, default: int) -> int:
        raw = cfg.get(key)
        if raw is None:
            return max(0, default)
        try:
            return max(0, int(raw))
        except (TypeError, ValueError):
            return max(0, default)

    return _cap_int("daily_council_cap", 10), _cap_int("hourly_council_cap", 4)


def council_cap_refusal_message() -> str:
    """Return a user-visible refusal when caps are exceeded, else empty string."""
    daily_cap, hourly_cap = _cap_limits()
    if daily_cap <= 0 and hourly_cap <= 0:
        return ""
    state = _normalize_state(_load_state())
    daily_count = int(state.get("daily_count", 0) or 0)
    hourly_count = int(state.get("hourly_count", 0) or 0)
    if daily_cap > 0 and daily_count >= daily_cap:
        return (
            f"Daily council limit reached ({daily_cap} per day). "
            "Try again tomorrow or raise the cap in Settings → General → Council mode."
        )
    if hourly_cap > 0 and hourly_count >= hourly_cap:
        return (
            f"Hourly council limit reached ({hourly_cap} per hour). "
            "Wait a bit or raise the cap in Settings → General → Council mode."
        )
    return ""


def record_council_spawn() -> None:
    """Record a successful council batch spawn against daily/hourly counters."""
    state = _normalize_state(_load_state())
    state["daily_count"] = int(state.get("daily_count", 0) or 0) + 1
    state["hourly_count"] = int(state.get("hourly_count", 0) or 0) + 1
    state["last_spawn_mono"] = time.monotonic()
    _save_state(state)


def reset_council_cap_state() -> None:
    """Test/operator helper — clears persisted cap counters."""
    try:
        path = _cap_state_path()
        if os.path.isfile(path):
            os.remove(path)
    except Exception:
        _log.debug("council cap state reset failed", exc_info=True)


def council_cap_snapshot() -> dict:
    """Read-only counters for diagnostics/tests."""
    state = _normalize_state(_load_state())
    daily_cap, hourly_cap = _cap_limits()
    return {
        "daily_count": int(state.get("daily_count", 0) or 0),
        "hourly_count": int(state.get("hourly_count", 0) or 0),
        "daily_cap": daily_cap,
        "hourly_cap": hourly_cap,
    }
