"""Dynamic domain whitelist for CORS and WebSocket origin checks.

Maintains an in-memory set of allowed origins that can be updated at
runtime via internal API calls from the platform, without restarting
the server.

When the set is empty (no ROOHAI_CORS_ORIGINS env var, typical OSS
usage), all origins are allowed.
"""

from __future__ import annotations

import logging
import os
import threading

logger = logging.getLogger(__name__)

_lock = threading.Lock()
_allowed_origins: set[str] = set()
_initialized: bool = False


def _normalize(origin: str) -> str:
    """Normalize origin: lowercase, strip trailing slash."""
    return origin.strip().lower().rstrip("/")


def seed_from_env() -> None:
    """Seed the whitelist from ROOHAI_CORS_ORIGINS env var.

    Called once at startup.  If the env var is unset or ``"*"``, the
    set stays empty which means "allow all".
    """
    global _initialized
    raw = os.environ.get("ROOHAI_CORS_ORIGINS", "").strip()
    with _lock:
        _allowed_origins.clear()
        if raw and raw != "*":
            for origin in raw.split(","):
                origin = _normalize(origin)
                if origin:
                    _allowed_origins.add(origin)
            logger.info(
                "Domain whitelist seeded with %d origin(s): %s",
                len(_allowed_origins),
                ", ".join(sorted(_allowed_origins)),
            )
        else:
            logger.info("Domain whitelist empty — all origins allowed (open mode)")
        _initialized = True


def is_origin_allowed(origin: str | None) -> bool:
    """Check if an origin is allowed.

    Returns True if:
    - The whitelist is empty (open mode / OSS default)
    - The origin is in the whitelist
    - No origin header was sent (non-browser client)
    """
    if not origin:
        return True
    with _lock:
        if not _allowed_origins:
            return True
        return _normalize(origin) in _allowed_origins


def add_origins(origins: list[str]) -> list[str]:
    """Add one or more origins. Returns the current full list."""
    with _lock:
        for o in origins:
            normalized = _normalize(o)
            if normalized:
                _allowed_origins.add(normalized)
                logger.info("Domain added: %s", normalized)
        return sorted(_allowed_origins)


def remove_origins(origins: list[str]) -> list[str]:
    """Remove one or more origins. Returns the current full list."""
    with _lock:
        for o in origins:
            normalized = _normalize(o)
            _allowed_origins.discard(normalized)
            logger.info("Domain removed: %s", normalized)
        return sorted(_allowed_origins)


def replace_origins(origins: list[str]) -> list[str]:
    """Replace the entire whitelist. Returns the new list."""
    with _lock:
        _allowed_origins.clear()
        for o in origins:
            normalized = _normalize(o)
            if normalized:
                _allowed_origins.add(normalized)
        logger.info(
            "Domain whitelist replaced with %d origin(s)",
            len(_allowed_origins),
        )
        return sorted(_allowed_origins)


def list_origins() -> list[str]:
    """Return the current whitelist as a sorted list."""
    with _lock:
        return sorted(_allowed_origins)
