"""Per-user/workspace session policy resolver."""

from __future__ import annotations

import json
import logging
from dataclasses import dataclass
from pathlib import Path
from typing import Any

from ieum.approval_policy import normalize_approval_policy

logger = logging.getLogger(__name__)


def _as_positive_int(value: Any) -> int | None:
    """Convert *value* to a positive integer, or ``None``."""
    if value is None:
        return None
    try:
        parsed = int(value)
    except (TypeError, ValueError):
        return None
    return parsed if parsed > 0 else None


@dataclass(frozen=True)
class SessionPolicy:
    """Resolved session policy for one user/workspace scope."""

    approval_policy: str
    timeout_minutes: int | None
    max_concurrent_sessions: int | None
    rate_limit_max_requests: int | None
    rate_limit_window_seconds: int | None
    sandbox_root: str | None


class SessionPolicyResolver:
    """Resolve effective policy from config defaults + optional JSON policy file."""

    def __init__(self, config: Any) -> None:
        self._config = config
        self._policy_file = (
            Path(config.session_policy_file).expanduser().resolve()
            if getattr(config, "session_policy_file", None)
            else None
        )
        self._policy_data: dict[str, Any] = {}
        self._policy_mtime_ns: int | None = None

    def _reload_if_needed(self) -> None:
        """Reload policy file if modified."""
        if self._policy_file is None:
            return
        if not self._policy_file.exists():
            self._policy_data = {}
            self._policy_mtime_ns = None
            return

        stat = self._policy_file.stat()
        if self._policy_mtime_ns == stat.st_mtime_ns:
            return

        try:
            loaded = json.loads(self._policy_file.read_text(encoding="utf-8"))
            if isinstance(loaded, dict):
                self._policy_data = loaded
            else:
                logger.warning(
                    "Session policy file must be a JSON object: %s",
                    self._policy_file,
                )
                self._policy_data = {}
            self._policy_mtime_ns = stat.st_mtime_ns
        except Exception:
            logger.warning(
                "Failed to parse session policy file: %s",
                self._policy_file,
                exc_info=True,
            )
            self._policy_data = {}
            self._policy_mtime_ns = stat.st_mtime_ns

    @staticmethod
    def _merge(base: dict[str, Any], override: dict[str, Any] | None) -> dict[str, Any]:
        """Return a new dict with *override* keys merged into *base*."""
        if not override:
            return dict(base)
        merged = dict(base)
        for key, value in override.items():
            if value is not None:
                merged[key] = value
        return merged

    def resolve(self, *, user_id: str, workspace_id: str) -> SessionPolicy:
        """Resolve policy for the given user/workspace pair."""
        self._reload_if_needed()

        defaults = {
            "approval_policy": normalize_approval_policy(
                getattr(self._config, "default_approval_policy", "manual")
            ),
            "timeout_minutes": getattr(self._config, "session_timeout_minutes", None),
            "max_concurrent_sessions": _as_positive_int(
                getattr(self._config, "max_sessions_per_user", None)
            ),
            "rate_limit_max_requests": _as_positive_int(
                getattr(self._config, "session_rate_limit_max_requests", None)
            ),
            "rate_limit_window_seconds": _as_positive_int(
                getattr(self._config, "session_rate_limit_window_seconds", None)
            ),
            "sandbox_root": getattr(self._config, "sandbox_root", None),
        }

        file_defaults = self._policy_data.get("defaults")
        merged = self._merge(defaults, file_defaults if isinstance(file_defaults, dict) else None)

        users = self._policy_data.get("users")
        if isinstance(users, dict):
            merged = self._merge(merged, users.get(user_id))

        workspaces = self._policy_data.get("workspaces")
        if isinstance(workspaces, dict):
            merged = self._merge(merged, workspaces.get(workspace_id))

        user_workspaces = self._policy_data.get("user_workspaces")
        if isinstance(user_workspaces, dict):
            for pair_key in (f"{user_id}/{workspace_id}", f"{user_id}:{workspace_id}"):
                if pair_key in user_workspaces:
                    merged = self._merge(merged, user_workspaces.get(pair_key))

        return SessionPolicy(
            approval_policy=normalize_approval_policy(merged.get("approval_policy")),
            timeout_minutes=_as_positive_int(merged.get("timeout_minutes")),
            max_concurrent_sessions=_as_positive_int(merged.get("max_concurrent_sessions")),
            rate_limit_max_requests=_as_positive_int(merged.get("rate_limit_max_requests")),
            rate_limit_window_seconds=_as_positive_int(merged.get("rate_limit_window_seconds")),
            sandbox_root=(
                str(Path(merged["sandbox_root"]).expanduser())
                if merged.get("sandbox_root")
                else None
            ),
        )
