"""Main proxy server for Robot Resources."""

import uuid as _uuid
from collections.abc import AsyncGenerator
from contextlib import asynccontextmanager

import structlog
from fastapi import Depends, FastAPI
from fastapi.middleware.cors import CORSMiddleware
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request

from robot_resources.config import get_settings
from robot_resources.proxy.handlers.compare import router as compare_router
from robot_resources.proxy.handlers.completions import router as completions_router
from robot_resources.proxy.handlers.config_handler import router as config_router
from robot_resources.proxy.handlers.messages import router as messages_router
from robot_resources.proxy.handlers.route import router as route_router
from robot_resources.proxy.handlers.stats import router as stats_router
from robot_resources.proxy.rate_limit import RateLimitMiddleware
from robot_resources.proxy.security import verify_api_key, verify_api_key_or_localhost
from robot_resources.proxy.timeout import TimeoutMiddleware

logger = structlog.get_logger()

_settings = get_settings()


@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
    """Manage server lifecycle: startup and graceful shutdown."""
    logger.info("server_starting", version="2.1.6", port=_settings.router_port)
    yield
    # Shutdown: close OutcomeDB if initialized
    from robot_resources.proxy.handlers.completions import _outcome_db

    if _outcome_db is not None:
        try:
            _outcome_db.close()
            logger.info("outcome_db_closed")
        except Exception:  # defensive-catch: shutdown cleanup must not raise
            logger.warning("outcome_db_close_error", exc_info=True)
    logger.info("server_shutdown_complete")


app = FastAPI(
    title="Robot Resources",
    description="AI Execution Intelligence Layer - Intelligent LLM cost optimization",
    version="2.1.6",
    lifespan=lifespan,
)


# ---------------------------------------------------------------------------
# CORS configuration (read once at startup)
# ---------------------------------------------------------------------------

_cors_origins = _settings.router_cors_origins.strip()

_CORS_METHODS = ["GET", "POST", "OPTIONS"]
_CORS_HEADERS = [
    "Content-Type", "Authorization", "x-api-key",
    "anthropic-version", "anthropic-beta",
]

if _cors_origins == "*":
    # Explicit opt-in: allow all origins.
    # Note: credentials cannot be used with wildcard origins (browser spec).
    app.add_middleware(
        CORSMiddleware,
        allow_origins=["*"],
        allow_credentials=False,
        allow_methods=_CORS_METHODS,
        allow_headers=_CORS_HEADERS,
    )
elif _cors_origins:
    # Specific origins (comma-separated)
    origins = [o.strip() for o in _cors_origins.split(",") if o.strip()]
    app.add_middleware(
        CORSMiddleware,
        allow_origins=origins,
        allow_credentials=True,
        allow_methods=_CORS_METHODS,
        allow_headers=_CORS_HEADERS,
    )
else:
    # Default: localhost only (any port).
    # Regex anchors prevent bypass via localhost.evil.com.
    app.add_middleware(
        CORSMiddleware,
        allow_origin_regex=r"^https?://localhost(:\d+)?$",
        allow_credentials=True,
        allow_methods=_CORS_METHODS,
        allow_headers=_CORS_HEADERS,
    )


# ---------------------------------------------------------------------------
# Correlation ID middleware
# ---------------------------------------------------------------------------


class CorrelationIdMiddleware(BaseHTTPMiddleware):
    """Inject a correlation ID into every request's structlog context."""

    async def dispatch(self, request: Request, call_next):  # noqa: ANN001
        request_id = request.headers.get(
            "x-request-id", str(_uuid.uuid4())
        )
        structlog.contextvars.clear_contextvars()
        structlog.contextvars.bind_contextvars(request_id=request_id)
        response = await call_next(request)
        response.headers["x-request-id"] = request_id
        return response


app.add_middleware(CorrelationIdMiddleware)

# Rate limiting middleware (reads limit from Settings per request)
app.add_middleware(RateLimitMiddleware)

# Global request timeout middleware
app.add_middleware(TimeoutMiddleware)


# ---------------------------------------------------------------------------
# Public endpoints (no auth)
# ---------------------------------------------------------------------------


def _health_check_db_path() -> str:
    """Return the DB path for health checks (extracted for testability)."""
    from robot_resources.tracking.db import DEFAULT_DB_PATH

    return str(DEFAULT_DB_PATH)


@app.get("/health")
async def health() -> dict:
    """Health check endpoint — always public for load balancers and probes."""
    checks: dict = {}
    healthy = True

    # Check 1: Database connectivity
    try:
        from robot_resources.tracking.db import OutcomeDB

        db = OutcomeDB(_health_check_db_path())
        db.connect()
        db.close()
        checks["database"] = "ok"
    except Exception:  # defensive-catch: health probe — any failure = unhealthy
        checks["database"] = "error"
        healthy = False

    # Check 2: Provider API key availability
    from robot_resources.keys import get_provider_key
    from robot_resources.proxy.handlers.completions import PROVIDER_REGISTRY

    providers_available = [
        name
        for name, (_, env_var) in PROVIDER_REGISTRY.items()
        if get_provider_key(env_var)
    ]
    checks["providers"] = providers_available
    if not providers_available:
        checks["providers_status"] = "passthrough_mode"
        # Not degraded — transparent proxy reads keys from request headers
    else:
        checks["providers_status"] = "ok"

    return {
        "status": "healthy" if healthy else "degraded",
        "version": "2.1.6",
        "checks": checks,
    }


# ---------------------------------------------------------------------------
# Protected endpoints (auth required when ROUTER_API_KEY is set)
# ---------------------------------------------------------------------------


@app.get("/v1/models", dependencies=[Depends(verify_api_key)])
async def list_models() -> dict:
    """List available models (includes all known models + auto)."""
    from robot_resources.routing import MODELS_DB

    data = [{"id": "auto", "object": "model", "owned_by": "robot-resources"}]
    for m in MODELS_DB:
        data.append({
            "id": m["name"],
            "object": "model",
            "owned_by": m["provider"],
        })
    return {"object": "list", "data": data}


# Completions router — protected with auth dependency
app.include_router(
    completions_router,
    dependencies=[Depends(verify_api_key)],
)

# Messages router (Anthropic format) — protected with auth dependency
app.include_router(
    messages_router,
    dependencies=[Depends(verify_api_key)],
)

# Stats router — protected with auth dependency
app.include_router(
    stats_router,
    dependencies=[Depends(verify_api_key)],
)

# Compare router — protected with auth dependency
app.include_router(
    compare_router,
    dependencies=[Depends(verify_api_key)],
)

# Route decision endpoint — lightweight, no upstream calls.
# Localhost bypass: OpenClaw plugin calls from 127.0.0.1 without auth.
app.include_router(
    route_router,
    dependencies=[Depends(verify_api_key_or_localhost)],
)

# Config router — protected with auth dependency
app.include_router(
    config_router,
    dependencies=[Depends(verify_api_key)],
)
