"""Server middleware — rate limiting and audit logging."""

from __future__ import annotations

import time
from collections import defaultdict

from fastapi import Request, Response
from starlette.middleware.base import BaseHTTPMiddleware

from kredo_drift.server.config import get_settings


class RateLimitMiddleware(BaseHTTPMiddleware):
    """Simple in-memory sliding window rate limiter."""

    def __init__(self, app):
        super().__init__(app)
        self._requests: dict[str, list[float]] = defaultdict(list)
        self._settings = get_settings()

    async def dispatch(self, request: Request, call_next) -> Response:
        client_ip = request.client.host if request.client else "unknown"
        now = time.time()
        window = 60.0
        limit = self._settings.rate_limit_per_minute

        # WHY sliding window (not fixed window): A fixed 60s window resets at
        # clock boundaries, allowing burst-then-reset abuse. The sliding window
        # always looks at the last 60 seconds of actual requests, providing
        # consistent throttling regardless of when the burst starts.
        self._requests[client_ip] = [
            t for t in self._requests[client_ip] if now - t < window
        ]

        if len(self._requests[client_ip]) >= limit:
            return Response(
                content='{"error": "Rate limit exceeded"}',
                status_code=429,
                media_type="application/json",
                headers={"Retry-After": "60"},
            )

        self._requests[client_ip].append(now)
        return await call_next(request)


class AuditLogMiddleware(BaseHTTPMiddleware):
    """Log API requests for audit trail."""

    async def dispatch(self, request: Request, call_next) -> Response:
        # Skip health checks
        if request.url.path == "/health":
            return await call_next(request)

        start = time.time()
        response = await call_next(request)
        duration_ms = (time.time() - start) * 1000

        # In production, write to audit_events table
        # For now, structured log data is available via the response
        response.headers["X-Request-Duration-Ms"] = f"{duration_ms:.1f}"
        return response
