"""
Permission Manager — Interactive permission prompts for remote actions.

When a room peer wants to run code, write files, or access models on the
local machine, the user is prompted for approval.  Supports one-shot
approval, per-peer "always" trust, and revocation.

Persisted at ``~/.brynq/permissions.json``.
"""

from __future__ import annotations

import json
import logging
import sys
import threading
from enum import Enum
from pathlib import Path
from typing import Optional

logger = logging.getLogger("brynq.security.permissions")

PERMISSIONS_FILE = Path.home() / ".brynq" / "permissions.json"


class Action(str, Enum):
    """Actions that require explicit permission from the user."""

    RUN_CODE = "run_code"
    WRITE_FILE = "write_file"
    READ_FILE = "read_file"
    ACCESS_MODELS = "access_models"
    DELETE_FILE = "delete_file"


class PermissionManager:
    """Interactive permission system for remote peer actions.

    Thread-safe.  All mutations are immediately flushed to disk.
    """

    def __init__(self, path: Path | str | None = None):
        self._path = Path(path) if path else PERMISSIONS_FILE
        self._lock = threading.Lock()
        self._grants: dict[str, list[str]] = {}  # peer_id -> [action, ...]
        self._load()

    # ── public API ────────────────────────────────────────────────────

    def ask_permission(
        self,
        peer_id: str,
        peer_name: str,
        action: Action | str,
        detail: str = "",
    ) -> bool:
        """Prompt the user to approve a remote action.

        Returns ``True`` if approved, ``False`` otherwise.
        If the peer+action already has an "always" grant, returns ``True``
        without prompting.
        """
        action_str = action.value if isinstance(action, Action) else action

        # Check existing "always" grants
        if self.has_permission(peer_id, action_str):
            return True

        # Prompt user on stdin/stdout
        detail_line = f" ({detail})" if detail else ""
        prompt = (
            f"\n[PERMISSION] [{peer_name}] wants to {action_str}{detail_line}.\n"
            f"  Allow? (y)es / (n)o / (a)lways: "
        )

        try:
            answer = input(prompt).strip().lower()
        except (EOFError, KeyboardInterrupt):
            return False

        if answer in ("a", "always"):
            self.grant_always(peer_id, action_str)
            return True
        if answer in ("y", "yes"):
            return True
        return False

    def has_permission(self, peer_id: str, action: str) -> bool:
        """Return whether *peer_id* has an "always" grant for *action*."""
        with self._lock:
            return action in self._grants.get(peer_id, [])

    def grant_always(self, peer_id: str, action: str) -> None:
        """Persistently grant *action* for *peer_id* (no future prompts)."""
        with self._lock:
            grants = self._grants.setdefault(peer_id, [])
            if action not in grants:
                grants.append(action)
            self._save()
        logger.info("Granted %s always for peer %s", action, peer_id)

    def revoke(self, peer_id: str) -> None:
        """Revoke all "always" permissions for *peer_id*."""
        with self._lock:
            self._grants.pop(peer_id, None)
            self._save()
        logger.info("Revoked all permissions for peer %s", peer_id)

    def revoke_action(self, peer_id: str, action: str) -> None:
        """Revoke a single action grant for a peer."""
        with self._lock:
            grants = self._grants.get(peer_id, [])
            if action in grants:
                grants.remove(action)
            if not grants:
                self._grants.pop(peer_id, None)
            self._save()

    def list_grants(self) -> dict[str, list[str]]:
        """Return a copy of all current grants."""
        with self._lock:
            return {k: list(v) for k, v in self._grants.items()}

    def clear_all(self) -> None:
        """Remove all grants (nuclear option)."""
        with self._lock:
            self._grants.clear()
            self._save()

    # ── persistence ───────────────────────────────────────────────────

    def _load(self) -> None:
        if self._path.exists():
            try:
                data = json.loads(self._path.read_text("utf-8"))
                self._grants = data.get("grants", {})
            except (json.JSONDecodeError, OSError) as exc:
                logger.warning("Failed to load permissions: %s", exc)
                self._grants = {}
        else:
            self._grants = {}

    def _save(self) -> None:
        self._path.parent.mkdir(parents=True, exist_ok=True)
        self._path.write_text(
            json.dumps({"grants": self._grants}, indent=2),
            encoding="utf-8",
        )
