"""Handler for GET /v1/config and PATCH /v1/config endpoints.

Provides a runtime config overlay that lets agents tune routing
behavior without restarting the proxy. Overrides are stored in
memory and cleared on restart — env vars remain the source of truth.
"""

from __future__ import annotations

from typing import Any

import structlog
from fastapi import APIRouter
from pydantic import BaseModel, field_validator

from robot_resources.config import get_settings
from robot_resources.routing.selector import (
    CAPABILITY_THRESHOLD,
    DEFAULT_BASELINE,
    MODELS_DB,
    get_model_by_name,
)

router = APIRouter()
logger = structlog.get_logger()

# ------------------------------------------------------------------
# Runtime config overlay (in-memory, cleared on restart)
# ------------------------------------------------------------------

_runtime_overrides: dict[str, Any] = {}

_VALID_PROVIDER_SCOPES = frozenset({"all", "anthropic", "openai", "google"})
_VALID_LOG_LEVELS = frozenset({"DEBUG", "INFO", "WARNING", "ERROR"})


def get_effective_config() -> dict[str, Any]:
    """Merge env-var defaults with runtime overrides.

    Returns the current effective config as a dict. Each field's value
    comes from _runtime_overrides if set, otherwise from env-var defaults.
    """
    settings = get_settings()
    defaults = {
        "provider_scope": settings.router_provider_scope,
        "capability_threshold": CAPABILITY_THRESHOLD,
        "baseline_model": DEFAULT_BASELINE,
        "log_level": settings.router_log_level,
    }
    merged = {**defaults, **_runtime_overrides}
    merged["overrides"] = sorted(_runtime_overrides.keys())
    return merged


def reset_overrides() -> None:
    """Clear all runtime overrides. Used in tests and on restart."""
    _runtime_overrides.clear()


# ------------------------------------------------------------------
# Pydantic model for PATCH body validation
# ------------------------------------------------------------------


class ConfigUpdate(BaseModel):
    """Partial config update. All fields optional — only provided fields
    are applied as overrides. Sending ``null`` for a field removes the
    override, reverting to the env-var default.
    """

    model_config = {"extra": "ignore"}

    provider_scope: str | None = None
    capability_threshold: float | None = None
    baseline_model: str | None = None
    log_level: str | None = None

    @field_validator("provider_scope")
    @classmethod
    def validate_provider_scope(cls, v: str | None) -> str | None:
        if v is not None and v not in _VALID_PROVIDER_SCOPES:
            msg = f"Invalid provider_scope '{v}'. Must be one of: {sorted(_VALID_PROVIDER_SCOPES)}"
            raise ValueError(msg)
        return v

    @field_validator("capability_threshold")
    @classmethod
    def validate_capability_threshold(cls, v: float | None) -> float | None:
        if v is not None and (v < 0.0 or v > 1.0):
            msg = f"capability_threshold must be between 0.0 and 1.0, got {v}"
            raise ValueError(msg)
        return v

    @field_validator("baseline_model")
    @classmethod
    def validate_baseline_model(cls, v: str | None) -> str | None:
        if v is not None and get_model_by_name(v, MODELS_DB) is None:
            msg = f"Model '{v}' not found in models database"
            raise ValueError(msg)
        return v

    @field_validator("log_level")
    @classmethod
    def validate_log_level(cls, v: str | None) -> str | None:
        if v is not None and v not in _VALID_LOG_LEVELS:
            msg = f"Invalid log_level '{v}'. Must be one of: {sorted(_VALID_LOG_LEVELS)}"
            raise ValueError(msg)
        return v


# ------------------------------------------------------------------
# Endpoints
# ------------------------------------------------------------------


@router.get("/v1/config")
def get_config() -> dict[str, Any]:
    """Get effective routing config (env-var defaults + runtime overrides).

    Returns all config fields with an ``overrides`` key listing which
    fields have been modified at runtime.
    """
    return get_effective_config()


@router.patch("/v1/config")
def patch_config(update: ConfigUpdate) -> dict[str, Any]:
    """Apply partial config updates as runtime overrides.

    Only provided (non-missing) fields are applied. Sending ``null``
    for a field removes the override, reverting to the env-var default.
    Overrides are in-memory only — restart clears all overrides.
    """
    provided = update.model_dump(exclude_unset=True)

    for field_name, value in provided.items():
        if value is None:
            # null → remove override, revert to default
            _runtime_overrides.pop(field_name, None)
        else:
            _runtime_overrides[field_name] = value

    logger.info("config_updated", overrides=list(_runtime_overrides.keys()))
    return get_effective_config()
