from __future__ import annotations

import json
import stat
from pathlib import Path

from .models import DesktopPassiveState


class JsonDesktopPassiveStorage:
    """Owner-only local state storage for passive desktop trust identity."""

    def __init__(self, file_path: str | Path) -> None:
        self._path = Path(file_path)

    @property
    def file_path(self) -> Path:
        return self._path

    def save_state(self, state: DesktopPassiveState) -> None:
        self._path.parent.mkdir(parents=True, exist_ok=True)
        payload = {
            "local_device_id": state.local_device_id,
            "project_identity_uuid": state.project_identity_uuid,
            "lineage_root_uuid": state.lineage_root_uuid,
            "workspace_root_hash": state.workspace_root_hash,
            "heartbeat_secret": state.heartbeat_secret,
            "trust_token": state.trust_token,
            "trust_token_expires_at": int(state.trust_token_expires_at),
            "trust_generation": int(state.trust_generation),
            "reconnect_generation": int(state.reconnect_generation),
            "trust_status": state.trust_status,
            "continuity_badges": list(state.continuity_badges),
            "last_heartbeat_ts": state.last_heartbeat_ts,
            "last_error": state.last_error,
        }
        self._path.write_text(
            json.dumps(payload, ensure_ascii=True, sort_keys=True, separators=(",", ":")),
            encoding="utf-8",
        )
        self._enforce_secure_permissions()

    def load_state(self) -> DesktopPassiveState | None:
        if not self._path.exists():
            return None
        try:
            data = json.loads(self._path.read_text(encoding="utf-8"))
            required = [
                "local_device_id",
                "project_identity_uuid",
                "lineage_root_uuid",
                "workspace_root_hash",
                "heartbeat_secret",
                "trust_token",
                "trust_token_expires_at",
            ]
            if not all(str(data.get(key, "")).strip() for key in required):
                return None
            return DesktopPassiveState(
                local_device_id=str(data["local_device_id"]),
                project_identity_uuid=str(data["project_identity_uuid"]),
                lineage_root_uuid=str(data["lineage_root_uuid"]),
                workspace_root_hash=str(data["workspace_root_hash"]),
                heartbeat_secret=str(data["heartbeat_secret"]),
                trust_token=str(data["trust_token"]),
                trust_token_expires_at=int(data["trust_token_expires_at"]),
                trust_generation=int(data.get("trust_generation", 0)),
                reconnect_generation=int(data.get("reconnect_generation", 0)),
                trust_status=str(data.get("trust_status", "paired")),
                continuity_badges=list(data.get("continuity_badges", [])),
                last_heartbeat_ts=(None if data.get("last_heartbeat_ts") is None else int(data.get("last_heartbeat_ts"))),
                last_error=(None if data.get("last_error") is None else str(data.get("last_error"))),
            )
        except Exception:
            return None

    def clear(self) -> None:
        if self._path.exists():
            self._path.unlink()

    def _enforce_secure_permissions(self) -> None:
        if self._path.exists():
            # On POSIX enforce 0600. On Windows chmod semantics differ; best-effort only.
            self._path.chmod(0o600)
            mode = stat.S_IMODE(self._path.stat().st_mode)
            if mode & 0o077:
                raise PermissionError("state_file_permissions_too_open")
