//! Middleware layer setup and configuration
//!
//! This module handles the application of all middleware layers including
//! authentication, metrics, security headers, and development features.

use axum::http::HeaderValue;
use axum::middleware::Next;
use axum::{extract::Request, middleware, response::Response};
#[cfg(feature = "auth")]
use {crate::auth::auth_middleware, std::sync::Arc, tower_cookies::CookieManagerLayer};

use crate::config::Config;
#[cfg(feature = "metrics")]
use crate::metrics::metrics_middleware;
use crate::security::headers::add_security_headers_with_config;

/// Apply all middleware layers to the application
#[allow(unused_variables)]
pub fn apply_middleware_layers(
    mut app: axum::Router,
    config: &Config,
    app_state: &crate::app_state::AppState,
    security_headers_config: crate::security::headers::SecurityHeadersConfig,
    #[cfg(feature = "auth")] jwt_service: Arc<crate::auth::JwtService>,
    #[cfg(feature = "auth")] auth_repository: Arc<crate::database::auth::AuthRepository>,
) -> axum::Router {
    // Insert AppState as a request extension so Leptos server functions can access
    // it via extract::<Extension<AppState>>() regardless of the outer Axum
    // state type.
    app = app.layer(axum::Extension(app_state.clone()));

    // Register Leptos server-function facades. Only needed when leptos-hydration
    // is active — htmx-ssr uses conventional Axum endpoints for OTP and contact
    // form submissions instead of Leptos server functions.
    #[cfg(all(feature = "auth", feature = "leptos-hydration"))]
    if let Some(otp_svc) = &app_state.auth_service.otp_service {
        use rustelo_components_leptos::auth::{OtpServiceExtension, OtpServiceFacade};

        use crate::auth::OtpServiceAdapter;
        let adapter = OtpServiceAdapter {
            otp_service: otp_svc.clone(),
            config: app_state.auth_service.otp_config.clone(),
            ws_server: Some(app_state.ws_server.clone()),
        };
        app = app.layer(axum::Extension(OtpServiceExtension(
            Arc::new(adapter) as Arc<dyn OtpServiceFacade>
        )));
    }

    #[cfg(all(feature = "email", feature = "leptos-hydration"))]
    {
        use std::sync::Arc;

        use rustelo_components_leptos::{ContactFormExtension, ContactFormFacade};

        use crate::email::ContactFormAdapter;
        let recipient = app_state.email_service.get_config().default_from.clone();
        let adapter = ContactFormAdapter {
            email_service: app_state.email_service.clone(),
            recipient,
        };
        app = app.layer(axum::Extension(ContactFormExtension(
            Arc::new(adapter) as Arc<dyn ContactFormFacade>
        )));
    }
    // Add metrics middleware if feature is enabled
    #[cfg(feature = "metrics")]
    {
        if config.app.enable_metrics {
            app = app.layer(axum::middleware::from_fn_with_state(
                app_state.clone(),
                metrics_middleware,
            ));
        }
    }

    // Add file-based RBAC middleware (must run AFTER auth injects AuthContext).
    // Layers are applied inside-out: auth is outer (runs first), RBAC is inner.
    // So RBAC must be added to the router BEFORE auth in this function.
    #[cfg(feature = "rbac")]
    {
        app = app.layer(axum::middleware::from_fn_with_state(
            app_state.file_rbac_config.clone(),
            crate::rbac::ncl_middleware::file_rbac_middleware,
        ));
    }

    // Add auth middleware if feature is enabled.
    // CookieManagerLayer MUST be the outer layer (applied last, runs first) so that
    // cookies are parsed before auth_middleware tries to extract them.
    #[cfg(feature = "auth")]
    {
        app = app
            .layer(axum::middleware::from_fn_with_state(
                (jwt_service, auth_repository),
                auth_middleware,
            ))
            .layer(CookieManagerLayer::new());
    }

    // Add security headers
    app = app.layer(axum::middleware::from_fn({
        let config = security_headers_config.clone();
        move |req: Request, next: Next| {
            let config = config.clone();
            async move {
                let response = next.run(req).await;
                add_security_headers_with_config(response, &config)
            }
        }
    }));

    // Add development cache headers
    app = app.layer(middleware::from_fn(dev_cache_middleware));

    app
}

/// Middleware to add no-cache headers for development and fix MIME types
pub async fn dev_cache_middleware(request: Request, next: Next) -> Response {
    // Get the path before moving the request
    let path = request.uri().path().to_string();
    let mut response = next.run(request).await;

    // Set proper MIME types for static files
    if path.starts_with("/public/") || path.starts_with("/styles/") || path.starts_with("/images/")
    {
        let headers = response.headers_mut();

        // Set proper content-type based on file extension
        if path.ends_with(".css") {
            headers.insert("Content-Type", HeaderValue::from_static("text/css"));
        } else if path.ends_with(".js") {
            headers.insert(
                "Content-Type",
                HeaderValue::from_static("application/javascript"),
            );
        } else if path.ends_with(".html") {
            headers.insert("Content-Type", HeaderValue::from_static("text/html"));
        } else if path.ends_with(".json") {
            headers.insert("Content-Type", HeaderValue::from_static("application/json"));
        } else if path.ends_with(".svg") {
            headers.insert("Content-Type", HeaderValue::from_static("image/svg+xml"));
        } else if path.ends_with(".png") {
            headers.insert("Content-Type", HeaderValue::from_static("image/png"));
        } else if path.ends_with(".jpg") || path.ends_with(".jpeg") {
            headers.insert("Content-Type", HeaderValue::from_static("image/jpeg"));
        }

        // Only add no-cache headers in development
        if cfg!(debug_assertions) {
            headers.insert(
                "Cache-Control",
                HeaderValue::from_static("no-cache, no-store, must-revalidate"),
            );
            headers.insert("Pragma", HeaderValue::from_static("no-cache"));
            headers.insert("Expires", HeaderValue::from_static("0"));
        }
    } else if path.starts_with("/pkg/") {
        let headers = response.headers_mut();

        // Set proper MIME types for WASM/JS files
        if path.ends_with(".wasm") {
            headers.insert("Content-Type", HeaderValue::from_static("application/wasm"));
        } else if path.ends_with(".js") {
            headers.insert(
                "Content-Type",
                HeaderValue::from_static("application/javascript"),
            );
        } else if path.ends_with(".css") {
            headers.insert("Content-Type", HeaderValue::from_static("text/css"));
        }

        // No-cache headers for development
        if cfg!(debug_assertions) {
            headers.insert(
                "Cache-Control",
                HeaderValue::from_static("no-cache, no-store, must-revalidate"),
            );
            headers.insert("Pragma", HeaderValue::from_static("no-cache"));
            headers.insert("Expires", HeaderValue::from_static("0"));
        }
    }

    response
}
