"""Runtime state persistence, kept separate from static config.

Static configuration lives in ``/etc/wolfpanel/agent.conf`` which is ALSO
consumed by systemd as an ``EnvironmentFile``, so it must stay clean, stable
KEY=VALUE data. Mutable runtime values — status, last error, the
registration-pending and revoked flags — live here in
``/var/lib/wolfpanel/state.json`` instead, so they never pollute the
EnvironmentFile and never break systemd parsing.

All persisted string values are sanitized to a single line so a multi-line
error message can never corrupt the on-disk state.
"""

from __future__ import annotations

import json
from pathlib import Path
from typing import Any

from logger import get_logger

log = get_logger("state")

_DEFAULT_STATE: dict[str, Any] = {
    "status": "",
    "last_error": "",
    "registration_pending": False,
    "revoked": False,
}


def _sanitize(value: Any) -> Any:
    """Collapse runtime strings to a safe, single-line value."""
    if isinstance(value, str):
        return " ".join(value.replace("\r", " ").replace("\n", " ").split())
    return value


def load_state(path: Path) -> dict[str, Any]:
    """Load runtime state, falling back to defaults on any error."""
    state = dict(_DEFAULT_STATE)
    try:
        data = json.loads(path.read_text(encoding="utf-8"))
        if isinstance(data, dict):
            state.update(data)
    except (OSError, ValueError):
        pass
    return state


def save_state(path: Path, state: dict[str, Any]) -> None:
    """Persist runtime state atomically. Never raises."""
    clean = {key: _sanitize(value) for key, value in state.items()}
    try:
        path.parent.mkdir(parents=True, exist_ok=True)
        tmp = path.with_name(path.name + ".tmp")
        tmp.write_text(json.dumps(clean, indent=2), encoding="utf-8")
        tmp.replace(path)
    except OSError as exc:  # pragma: no cover - environment dependent
        log.warning("failed to persist state to %s: %s", path, exc)


def update_state(path: Path, **changes: Any) -> dict[str, Any]:
    """Merge ``changes`` into the persisted state and write it back."""
    state = load_state(path)
    for key, value in changes.items():
        state[key] = _sanitize(value)
    save_state(path, state)
    return state
