"""External access control policy loader for Tribunal.

Policy is loaded in priority order:
1. ``~/.tribunal/access-policy.json`` — org-managed fleet config
2. Remote URL from ``TRIBUNAL_POLICY_URL`` env var — fetched with 30s timeout, cached 24h
3. Hardcoded defaults — backward compatible

Schema for ``access-policy.json``:
    {
        "authorized_org_uuids": ["<uuid>", ...],
        "authorized_domains": ["example.com", ...],
        "authorized_emails": ["user@example.com", ...]
    }
"""

from __future__ import annotations

import json
import os
import time
import urllib.error
import urllib.request
from pathlib import Path
from typing import Any

# Hardcoded defaults (backward compatible)
_DEFAULT_ORG_UUIDS: list[str] = [
    "e6a10da9-0291-43c0-9587-83af6c68725d",
    "31e78506-3d5c-431a-b463-4ad537468452",
]
_DEFAULT_DOMAINS: list[str] = [
    "thebotclub.ai",
]
_DEFAULT_EMAILS: list[str] = []
# No hardcoded personal emails. Configure authorized_emails via:
#   ~/.tribunal/access-policy.json  or  TRIBUNAL_POLICY_URL env var.

_POLICY_CACHE_FILE = ".policy-cache.json"
_POLICY_CACHE_TTL = 24 * 60 * 60  # 24 hours


def _get_tribunal_home() -> Path:
    from launcher.config import get_tribunal_home
    return get_tribunal_home()


def _load_local_policy() -> dict[str, Any] | None:
    """Load policy from ~/.tribunal/access-policy.json if it exists."""
    policy_path = _get_tribunal_home() / "access-policy.json"
    if not policy_path.is_file():
        return None
    try:
        data = json.loads(policy_path.read_text(encoding="utf-8"))
        if isinstance(data, dict):
            return data
    except (json.JSONDecodeError, OSError):
        pass
    return None


def _load_remote_policy(url: str) -> dict[str, Any] | None:
    """Fetch policy from a remote URL (30s timeout). Returns None on failure."""
    try:
        req = urllib.request.Request(url, headers={"User-Agent": "tribunal-tribunal/1.0"})
        with urllib.request.urlopen(req, timeout=30.0) as resp:
            if resp.status == 200:
                data = json.loads(resp.read().decode("utf-8"))
                if isinstance(data, dict):
                    return data
    except (urllib.error.URLError, json.JSONDecodeError, OSError, TimeoutError):
        pass
    return None


def _load_cached_remote_policy() -> dict[str, Any] | None:
    """Load cached remote policy if not expired."""
    cache_path = _get_tribunal_home() / _POLICY_CACHE_FILE
    if not cache_path.is_file():
        return None
    try:
        cache = json.loads(cache_path.read_text(encoding="utf-8"))
        if time.time() - cache.get("cached_at", 0) < _POLICY_CACHE_TTL:
            return cache.get("policy")
    except (json.JSONDecodeError, OSError):
        pass
    return None


def _save_remote_policy_cache(policy: dict[str, Any]) -> None:
    """Save fetched remote policy to disk cache."""
    cache_path = _get_tribunal_home() / _POLICY_CACHE_FILE
    cache_path.parent.mkdir(parents=True, exist_ok=True)
    try:
        cache_path.write_text(
            json.dumps({"cached_at": time.time(), "policy": policy}, indent=2),
            encoding="utf-8",
        )
        import stat
        cache_path.chmod(stat.S_IRUSR | stat.S_IWUSR)
    except OSError:
        pass


def load_access_policy() -> dict[str, Any]:
    """Load the effective access control policy.

    Returns a dict with keys:
        - ``authorized_org_uuids``: list[str]
        - ``authorized_domains``: list[str]
        - ``authorized_emails``: list[str]
    """
    # 1. Local org-managed config
    policy = _load_local_policy()
    if policy is not None:
        return _normalise_policy(policy)

    # 2. Remote URL (from env var), with 24h cache
    policy_url = os.environ.get("TRIBUNAL_POLICY_URL", os.environ.get("MERIDIAN_POLICY_URL", "")).strip()  # MERIDIAN_POLICY_URL kept for backward compat
    if policy_url:
        cached = _load_cached_remote_policy()
        if cached is not None:
            return _normalise_policy(cached)
        remote = _load_remote_policy(policy_url)
        if remote is not None:
            _save_remote_policy_cache(remote)
            return _normalise_policy(remote)

    # 3. Hardcoded defaults
    return {
        "authorized_org_uuids": list(_DEFAULT_ORG_UUIDS),
        "authorized_domains": list(_DEFAULT_DOMAINS),
        "authorized_emails": list(_DEFAULT_EMAILS),
    }


def _normalise_policy(raw: dict[str, Any]) -> dict[str, Any]:
    """Ensure all expected keys exist with list values."""
    return {
        "authorized_org_uuids": list(raw.get("authorized_org_uuids", _DEFAULT_ORG_UUIDS)),
        "authorized_domains": list(raw.get("authorized_domains", _DEFAULT_DOMAINS)),
        "authorized_emails": list(raw.get("authorized_emails", _DEFAULT_EMAILS)),
    }
