from __future__ import annotations

import json
import os
import secrets
from pathlib import Path
from typing import Any


class DeviceCredentialStore:
    """Validate one provisioned device and bind its id on first use."""

    def __init__(self, path: str | Path) -> None:
        self.path = Path(path).expanduser()

    def _load(self) -> dict[str, Any] | None:
        try:
            value = json.loads(self.path.read_text(encoding="utf-8"))
        except (FileNotFoundError, json.JSONDecodeError, OSError):
            return None
        return value if isinstance(value, dict) else None

    def authenticate(self, device_id: str, device_token: str) -> bool:
        credential = self._load()
        if credential is None:
            return False
        expected_token = credential.get("device_token")
        expected_id = credential.get("device_id", "")
        if not isinstance(expected_token, str) or not isinstance(expected_id, str):
            return False
        if not secrets.compare_digest(device_token, expected_token):
            return False
        if expected_id:
            return secrets.compare_digest(device_id, expected_id)

        # A high-entropy token authorizes the first physical device to bind its
        # derived MAC-based id. After this atomic write, both values must match.
        credential["device_id"] = device_id
        temporary = self.path.with_name(f".{self.path.name}.{os.getpid()}.tmp")
        temporary.write_text(json.dumps(credential, indent=2) + "\n", encoding="utf-8")
        os.chmod(temporary, 0o600)
        temporary.replace(self.path)
        return True

    def authenticate_pi(self, token: str) -> bool:
        credential = self._load()
        if credential is None:
            return False
        expected = credential.get("pi_token")
        return isinstance(expected, str) and bool(expected) and secrets.compare_digest(token, expected)
