//! Main application router creation
//!
//! This module handles the creation of the main application router with all routes,
//! static file serving, and dynamic route handling.

use super::handlers::fallback_handler;
use crate::app_state::AppState;
use crate::config::{Config, Environment};
use crate::run::render_ssr_with_context;
use crate::utils::lang_detection;
use axum::{extract::State, http::HeaderMap, response::IntoResponse, routing::get, Router};
use leptos::prelude::*;
use rustelo_core_lib::routing::get_routing_config;
use std::collections::HashSet;
use std::path::PathBuf;
use tower_http::services::ServeDir;

/// Create the main application router with all routes and static file serving
pub fn create_main_router(
    config: &Config,
    leptos_options: LeptosOptions,
    app_state: AppState,
) -> Result<axum::Router, Box<dyn std::error::Error>> {
    // Create a custom route handler for the root path instead of automatic Leptos routing
    let app = Router::new()
        .route(
            "/",
            get(
                move |State(opts): State<LeptosOptions>, headers: HeaderMap| async move {
                    // Detect language preference from cookies/headers
                    let detected_lang = lang_detection::detect_ssr_language("/", &headers);

                    // If Spanish is preferred and we're at root, redirect to Spanish home
                    if detected_lang == "es" {
                        let spanish_home = lang_detection::get_localized_url("/", "es");
                        if spanish_home != "/" {
                            return axum::response::Response::builder()
                                .status(302) // Temporary redirect
                                .header("Location", spanish_home)
                                .body(axum::body::Body::empty())
                                .unwrap()
                                .into_response();
                        }
                    }

                    // Otherwise render the page normally
                    axum::response::Html(render_ssr_with_context(opts, Some("/".to_string())))
                        .into_response()
                },
            ),
        )
        // Add specific route for favicon.ico at root level
        .route(
            "/favicon.ico",
            get(|| async {
                match tokio::fs::read("public/favicon.ico").await {
                    Ok(content) => axum::response::Response::builder()
                        .header("Content-Type", "image/x-icon")
                        .header("Cache-Control", "public, max-age=86400")
                        .body(axum::body::Body::from(content))
                        .unwrap()
                        .into_response(),
                    Err(_) => axum::response::Response::builder()
                        .status(404)
                        .body(axum::body::Body::empty())
                        .unwrap()
                        .into_response(),
                }
            }),
        )
        // Add specific route for kitdigital.html to serve from public directory
        .route(
            "/kitdigital.html",
            get(|| async {
                match tokio::fs::read_to_string("public/kitdigital.html").await {
                    Ok(content) => axum::response::Html(content).into_response(),
                    Err(_) => axum::response::Response::builder()
                        .status(404)
                        .body(axum::body::Body::from("404 - File not found"))
                        .unwrap()
                        .into_response(),
                }
            }),
        )
        // Add dynamic routes from SSR routing configuration
        .merge(create_dynamic_routes(leptos_options.clone(), &[]))
        .nest_service(
            "/pkg",
            ServeDir::new("target/site/pkg").append_index_html_on_directories(false),
        )
        .nest_service(
            "/public",
            ServeDir::new(&config.server_dirs.public_dir)
                .precompressed_gzip()
                .precompressed_br()
                .precompressed_deflate(),
        )
        .nest_service(
            "/styles",
            ServeDir::new(PathBuf::from(&config.server_dirs.public_dir).join("styles"))
                .precompressed_gzip()
                .precompressed_br()
                .precompressed_deflate(),
        )
        .nest_service(
            "/logos",
            ServeDir::new(PathBuf::from(&config.server_dirs.public_dir).join("logos"))
                .precompressed_gzip()
                .precompressed_br()
                .precompressed_deflate(),
        )
        .nest_service(
            "/images",
            ServeDir::new(PathBuf::from(&config.server_dirs.public_dir).join("images"))
                .precompressed_gzip()
                .precompressed_br()
                .precompressed_deflate(),
        )
        .nest_service(
            "/kitdigital",
            ServeDir::new(PathBuf::from(&config.server_dirs.public_dir).join("kitdigital"))
                .precompressed_gzip()
                .precompressed_br()
                .precompressed_deflate(),
        )
        .nest_service(
            "/js",
            ServeDir::new(PathBuf::from(&config.server_dirs.public_dir).join("js"))
                .precompressed_gzip()
                .precompressed_br()
                .precompressed_deflate(),
        )
        .nest_service(
            "/css",
            ServeDir::new(PathBuf::from(&config.server_dirs.public_dir).join("css"))
                .precompressed_gzip()
                .precompressed_br()
                .precompressed_deflate(),
        );

    // Conditionally serve /assets/ directory only in development environment
    let app = if config.server.environment == Environment::Development {
        let assets_path = std::env::var("SITE_ASSETS_PATH").unwrap_or_else(|_| {
            let site_root = std::env::var("SITE_ROOT_PATH").unwrap_or_else(|_| "site".to_string());
            format!("{}/assets", site_root)
        });
        app.nest_service(
            "/assets",
            ServeDir::new(assets_path)
                .precompressed_gzip()
                .precompressed_br()
                .precompressed_deflate(),
        )
    } else {
        app
    };

    let app = app.nest_service(
        &std::env::var("SITE_SERVER_CONTENT_URL").unwrap_or_else(|_| "/".to_string()),
        ServeDir::new(format!(
            "target/site/{}",
            std::env::var("SITE_SERVER_ROOT_CONTENT").unwrap_or_else(|_| "".to_string())
        ))
        .precompressed_gzip()
        .precompressed_br()
        .precompressed_deflate(),
    );

    // Content resources route will be added by dynamic content routes

    // Content routes are now handled by SITE_SERVER_CONTENT_URL configuration above

    let app = app
        // Add fallback handler for dynamic routes (content posts, etc.)
        .fallback(fallback_handler)
        .with_state(app_state.leptos_options.clone());

    Ok(app)
}

/// Generate dynamic routes from SSR routing configuration
/// Only adds routes that aren't already handled by Leptos routes
pub fn create_dynamic_routes(
    leptos_options: LeptosOptions,
    leptos_routes: &[leptos_axum::AxumRouteListing],
) -> Router<LeptosOptions> {
    let routing_config = get_routing_config();
    let mut router = Router::new();

    // Collect Leptos route paths to avoid conflicts
    let leptos_paths: HashSet<String> =
        leptos_routes.iter().map(|r| r.path().to_string()).collect();

    tracing::debug!("Leptos routes already registered: {:?}", leptos_paths);

    // Get all routes from the SSR configuration, excluding those handled by Leptos
    let all_routes = &routing_config.routes;

    for route_config in all_routes {
        let path = route_config.path.clone();

        // Skip routes that are already handled by Leptos, manually handled, or served as static files
        if leptos_paths.contains(&path) || path == "/" || path == "/content" {
            tracing::debug!(
                "Skipping route '{}' - already handled by Leptos or custom handler",
                path
            );
            continue;
        }

        // Skip external URLs and routes that don't start with '/'
        if route_config.is_external.unwrap_or(false) || !path.starts_with('/') {
            tracing::debug!(
                "Skipping external route '{}' - not suitable for internal router",
                path
            );
            continue;
        }

        let opts = leptos_options.clone();

        tracing::debug!("Adding dynamic SSR route: {}", path);

        // Check if this is a parametric route that needs path extraction
        if path.contains('{') && path.contains('}') {
            // Handle parametric routes - extract actual path from URI
            router = router.route(
                &path,
                axum::routing::get({
                    let template_path = path.clone();
                    let opts = opts.clone();
                    move |uri: axum::http::Uri, headers: HeaderMap| {
                        let template_path = template_path.clone();
                        let opts = opts.clone();
                        async move {
                            // Extract the actual request path from the URI
                            let actual_path = uri.path().to_string();

                            tracing::info!(
                                "🔧 Parametric route - template: '{}', actual: '{}'",
                                template_path,
                                actual_path
                            );

                            // Use the shared routing engine to resolve the route first
                            let route_resolution = rustelo_core_lib::routing::engine::resolve_route(&actual_path);

                            // Check if the routing engine provides a specific component resolution
                            if route_resolution.exists && route_resolution.component.is_some() {
                                tracing::info!(
                                    "🔧 Parametric route using routing engine resolution: {:?} with parameters: {:?}",
                                    route_resolution.component,
                                    route_resolution.parameters
                                );

                                // Detect language preference from cookies/headers for this route
                                let detected_lang = crate::utils::lang_detection::detect_ssr_language(
                                    &actual_path,
                                    &headers,
                                );

                                // Check if we need to redirect to the localized version (language-agnostic)
                                if let Some(localized_url) = crate::utils::lang_detection::check_language_redirect(
                                    &actual_path,
                                    &detected_lang,
                                    &headers
                                ) {
                                    tracing::debug!(
                                        "Redirecting {} to localized version ({}): {}",
                                        actual_path,
                                        detected_lang,
                                        localized_url
                                    );
                                    return axum::response::Response::builder()
                                        .status(302) // Temporary redirect
                                        .header("Location", localized_url)
                                        .body(axum::body::Body::empty())
                                        .unwrap()
                                        .into_response();
                                }

                                // Use routing engine's path with standard shell rendering
                                // This ensures proper header, footer, and layout matching
                                return axum::response::Html(render_ssr_with_context(opts, Some(actual_path)))
                                    .into_response();
                            }

                            // Fallback to standard TOML-based parametric routing
                            tracing::info!("🔧 Parametric route falling back to standard TOML routing");

                            // Detect language preference from cookies/headers for this route
                            let detected_lang = crate::utils::lang_detection::detect_ssr_language(
                                &actual_path,
                                &headers,
                            );

                            // Check if we need to redirect to the localized version (language-agnostic)
                            if let Some(localized_url) = crate::utils::lang_detection::check_language_redirect(
                                &actual_path,
                                &detected_lang,
                                &headers
                            ) {
                                tracing::debug!(
                                    "Redirecting {} to localized version ({}): {}",
                                    actual_path,
                                    detected_lang,
                                    localized_url
                                );
                                return axum::response::Response::builder()
                                    .status(302) // Temporary redirect
                                    .header("Location", localized_url)
                                    .body(axum::body::Body::empty())
                                    .unwrap()
                                    .into_response();
                            }

                            // Use the actual request path for SSR rendering
                            axum::response::Html(render_ssr_with_context(opts, Some(actual_path)))
                                .into_response()
                        }
                    }
                }),
            );
        } else {
            // Handle regular non-parametric routes
            tracing::info!("🛤️  Registering non-parametric route: {}", path);

            router = router.route(
                &path,
                axum::routing::get({
                    let path = path.clone();
                    let opts = opts.clone();
                    move |headers: HeaderMap| {
                        let path = path.clone();
                        let opts = opts.clone();
                        async move {
                            // Detect language preference from cookies/headers for this route
                            let detected_lang =
                                crate::utils::lang_detection::detect_ssr_language(&path, &headers);

                            // Check if we need to redirect to the localized version (language-agnostic)
                            if let Some(localized_url) =
                                crate::utils::lang_detection::check_language_redirect(
                                    &path,
                                    &detected_lang,
                                    &headers,
                                )
                            {
                                tracing::debug!(
                                    "Redirecting {} to localized version ({}): {}",
                                    path,
                                    detected_lang,
                                    localized_url
                                );
                                return axum::response::Response::builder()
                                    .status(302) // Temporary redirect
                                    .header("Location", localized_url)
                                    .body(axum::body::Body::empty())
                                    .unwrap()
                                    .into_response();
                            }

                            // Otherwise render the page normally
                            axum::response::Html(render_ssr_with_context(opts, Some(path)))
                                .into_response()
                        }
                    }
                }),
            );

            tracing::info!("✅ Successfully registered route: {}", path);
        }
    }

    // Add additional paths that might not be in the config yet
    // but are used by the application (also checking for conflicts)
    let additional_paths = vec!["/daisyui", "/features-demo"]; // /user is now handled by TOML config

    for path in additional_paths {
        // Skip if already handled by Leptos
        if leptos_paths.contains(path) {
            tracing::debug!(
                "Skipping additional route '{}' - already handled by Leptos",
                path
            );
            continue;
        }

        let opts = leptos_options.clone();

        tracing::debug!("Adding additional route: {}", path);

        router = router.route(
            path,
            axum::routing::get({
                let path = path.to_string();
                let opts = opts.clone();
                move || {
                    let path = path.clone();
                    let opts = opts.clone();
                    async move { axum::response::Html(render_ssr_with_context(opts, Some(path))) }
                }
            }),
        );
    }

    router.with_state(leptos_options)
}
