//! HTTP middleware components.

use crate::{
    auth::{ApiKeyTier, AuthService},
    config::{CorsConfig, RateLimitConfig},
    error::ApiError,
};
use axum::{
    extract::{ConnectInfo, Request, State},
    http::{HeaderName, HeaderValue, Method},
    middleware::Next,
    response::Response,
};
use governor::{
    clock::DefaultClock,
    state::{InMemoryState, NotKeyed},
    Quota, RateLimiter,
};
use std::{
    collections::HashMap,
    net::SocketAddr,
    num::NonZeroU32,
    sync::Arc,
    time::{Duration, Instant},
};
use tokio::sync::RwLock;
use tower_http::cors::{Any, CorsLayer};
use tracing::{info, warn, Span};
use uuid::Uuid;

/// Request ID header name.
pub const REQUEST_ID_HEADER: &str = "X-Request-Id";

/// Rate limit headers.
pub const RATE_LIMIT_LIMIT: &str = "X-RateLimit-Limit";
pub const RATE_LIMIT_REMAINING: &str = "X-RateLimit-Remaining";
pub const RATE_LIMIT_RESET: &str = "X-RateLimit-Reset";

/// Build CORS layer from configuration.
pub fn build_cors_layer(config: &CorsConfig) -> CorsLayer {
    let mut cors = CorsLayer::new();

    // Allowed origins
    if config.allowed_origins.is_empty() {
        cors = cors.allow_origin(Any);
    } else {
        let origins: Vec<HeaderValue> = config
            .allowed_origins
            .iter()
            .filter_map(|o| o.parse().ok())
            .collect();
        cors = cors.allow_origin(origins);
    }

    // Allowed methods
    let methods: Vec<Method> = config
        .allowed_methods
        .iter()
        .filter_map(|m| m.parse().ok())
        .collect();
    cors = cors.allow_methods(methods);

    // Allowed headers
    let headers: Vec<HeaderName> = config
        .allowed_headers
        .iter()
        .filter_map(|h| h.parse().ok())
        .collect();
    cors = cors.allow_headers(headers);

    // Expose headers
    let expose: Vec<HeaderName> = config
        .expose_headers
        .iter()
        .filter_map(|h| h.parse().ok())
        .collect();
    cors = cors.expose_headers(expose);

    // Credentials
    if config.allow_credentials {
        cors = cors.allow_credentials(true);
    }

    // Max age
    cors = cors.max_age(Duration::from_secs(config.max_age));

    cors
}

/// Request ID middleware - adds unique ID to each request.
pub async fn request_id_middleware(mut request: Request, next: Next) -> Response {
    let request_id = request
        .headers()
        .get(REQUEST_ID_HEADER)
        .and_then(|v| v.to_str().ok())
        .map(|s| s.to_string())
        .unwrap_or_else(|| Uuid::new_v4().to_string());

    request
        .headers_mut()
        .insert(REQUEST_ID_HEADER, request_id.parse().unwrap());

    // Add to tracing span
    Span::current().record("request_id", &request_id);

    let mut response = next.run(request).await;

    response
        .headers_mut()
        .insert(REQUEST_ID_HEADER, request_id.parse().unwrap());

    response
}

/// Request timing middleware - tracks request duration.
pub async fn timing_middleware(request: Request, next: Next) -> Response {
    let start = Instant::now();
    let method = request.method().clone();
    let uri = request.uri().path().to_string();

    let response = next.run(request).await;

    let duration = start.elapsed();
    let status = response.status();

    // Log request
    if status.is_success() {
        info!(
            method = %method,
            uri = %uri,
            status = %status.as_u16(),
            duration_ms = %duration.as_millis(),
            "Request completed"
        );
    } else {
        warn!(
            method = %method,
            uri = %uri,
            status = %status.as_u16(),
            duration_ms = %duration.as_millis(),
            "Request failed"
        );
    }

    // Update metrics
    metrics::counter!("http_requests_total", "method" => method.to_string(), "status" => status.as_u16().to_string()).increment(1);
    metrics::histogram!("http_request_duration_seconds", "method" => method.to_string()).record(duration.as_secs_f64());

    response
}

/// Rate limiter state.
pub struct RateLimiterState {
    config: RateLimitConfig,
    /// Per-IP rate limiters
    ip_limiters: RwLock<HashMap<String, Arc<RateLimiter<NotKeyed, InMemoryState, DefaultClock>>>>,
    /// Per-API-key rate limiters
    key_limiters: RwLock<HashMap<String, Arc<RateLimiter<NotKeyed, InMemoryState, DefaultClock>>>>,
}

impl RateLimiterState {
    /// Create a new rate limiter state.
    pub fn new(config: RateLimitConfig) -> Self {
        Self {
            config,
            ip_limiters: RwLock::new(HashMap::new()),
            key_limiters: RwLock::new(HashMap::new()),
        }
    }

    /// Get or create a rate limiter for an IP.
    pub async fn get_ip_limiter(&self, ip: &str) -> Arc<RateLimiter<NotKeyed, InMemoryState, DefaultClock>> {
        {
            let limiters = self.ip_limiters.read().await;
            if let Some(limiter) = limiters.get(ip) {
                return limiter.clone();
            }
        }

        let rpm = NonZeroU32::new(self.config.default_rpm).unwrap_or(NonZeroU32::new(60).unwrap());
        let quota = Quota::per_minute(rpm).allow_burst(
            NonZeroU32::new(self.config.burst_size).unwrap_or(NonZeroU32::new(10).unwrap()),
        );
        let limiter = Arc::new(RateLimiter::direct(quota));

        let mut limiters = self.ip_limiters.write().await;
        limiters.insert(ip.to_string(), limiter.clone());
        limiter
    }

    /// Get or create a rate limiter for an API key.
    pub async fn get_key_limiter(&self, key_id: &str, tier: ApiKeyTier) -> Arc<RateLimiter<NotKeyed, InMemoryState, DefaultClock>> {
        {
            let limiters = self.key_limiters.read().await;
            if let Some(limiter) = limiters.get(key_id) {
                return limiter.clone();
            }
        }

        let tier_config = match tier {
            ApiKeyTier::Free => &self.config.tiers.free,
            ApiKeyTier::Developer => &self.config.tiers.developer,
            ApiKeyTier::Pro => &self.config.tiers.pro,
            ApiKeyTier::Enterprise => &self.config.tiers.enterprise,
        };

        // Enterprise tier has unlimited requests
        if tier_config.rpm == 0 {
            let quota = Quota::per_second(NonZeroU32::new(u32::MAX).unwrap());
            let limiter = Arc::new(RateLimiter::direct(quota));
            let mut limiters = self.key_limiters.write().await;
            limiters.insert(key_id.to_string(), limiter.clone());
            return limiter;
        }

        let rpm = NonZeroU32::new(tier_config.rpm).unwrap_or(NonZeroU32::new(60).unwrap());
        let quota = Quota::per_minute(rpm).allow_burst(
            NonZeroU32::new(tier_config.burst).unwrap_or(NonZeroU32::new(10).unwrap()),
        );
        let limiter = Arc::new(RateLimiter::direct(quota));

        let mut limiters = self.key_limiters.write().await;
        limiters.insert(key_id.to_string(), limiter.clone());
        limiter
    }
}

/// Rate limiting middleware.
pub async fn rate_limit_middleware(
    State(state): State<Arc<RateLimiterState>>,
    ConnectInfo(addr): ConnectInfo<SocketAddr>,
    request: Request,
    next: Next,
) -> Result<Response, ApiError> {
    if !state.config.enabled {
        return Ok(next.run(request).await);
    }

    let ip = addr.ip().to_string();
    let limiter = state.get_ip_limiter(&ip).await;

    match limiter.check() {
        Ok(_) => {
            let mut response = next.run(request).await;

            // Add rate limit headers
            response.headers_mut().insert(
                RATE_LIMIT_LIMIT,
                state.config.default_rpm.to_string().parse().unwrap(),
            );

            Ok(response)
        }
        Err(_not_until) => {
            // Use a fixed retry time since we can't easily convert to quanta's instant
            let retry_after = 60; // Default to 60 seconds

            Err(ApiError::TooManyRequests {
                retry_after,
            })
        }
    }
}

/// Authentication middleware that injects auth service.
pub async fn auth_middleware(
    State(auth_service): State<Arc<AuthService>>,
    mut request: Request,
    next: Next,
) -> Response {
    // Inject auth service into request extensions
    request.extensions_mut().insert((*auth_service).clone());
    next.run(request).await
}

/// API version middleware - validates version prefix.
pub async fn version_middleware(
    request: Request,
    next: Next,
) -> Result<Response, ApiError> {
    let path = request.uri().path();

    // Skip version check for health, metrics, and docs
    if path == "/health"
        || path.starts_with("/metrics")
        || path.starts_with("/docs")
        || path.starts_with("/swagger")
        || path.starts_with("/openapi")
    {
        return Ok(next.run(request).await);
    }

    // Check for version prefix
    if !path.starts_with("/v1/") && !path.starts_with("/v2/") {
        return Err(ApiError::BadRequest(
            "API version required. Use /v1/ prefix".to_string(),
        ));
    }

    Ok(next.run(request).await)
}

/// Security headers middleware.
pub async fn security_headers_middleware(request: Request, next: Next) -> Response {
    let mut response = next.run(request).await;

    let headers = response.headers_mut();

    // Prevent XSS
    headers.insert(
        "X-Content-Type-Options",
        "nosniff".parse().unwrap(),
    );

    // Prevent clickjacking
    headers.insert(
        "X-Frame-Options",
        "DENY".parse().unwrap(),
    );

    // Enable XSS filter
    headers.insert(
        "X-XSS-Protection",
        "1; mode=block".parse().unwrap(),
    );

    // Strict transport security (HTTPS)
    headers.insert(
        "Strict-Transport-Security",
        "max-age=31536000; includeSubDomains".parse().unwrap(),
    );

    // Content security policy
    headers.insert(
        "Content-Security-Policy",
        "default-src 'self'".parse().unwrap(),
    );

    response
}
