//! Modern TOML-based routing configuration
//!
//! This module provides the new routing configuration system that works
//! entirely with TOML-based route definitions and supports any number of
//! languages dynamically.
//!
//! ARCHITECTURE: This module contains the core route loading logic and
//! configuration management. It uses core_types for shared types/caches (NO
//! circular dependency).

use rustelo_language::get_language_registry;

use super::core_types::{RouteConfigToml, RoutesConfig, ROUTES_CACHE};

// ============================================================================
// ROUTE LOADING (Core implementation - used by all routing modules)
// ============================================================================

/// Load routes from the routes/ directory using SITE_CONFIG_PATH environment
/// variable
#[cfg(all(not(target_arch = "wasm32"), not(has_generated_resources)))]
fn load_routes_from_directory() -> Option<RoutesConfig> {
    use std::fs;
    use std::path::PathBuf;

    // Get the config path from environment variable
    let config_path = match std::env::var("SITE_CONFIG_PATH") {
        Ok(path) => path,
        Err(_) => {
            // Fallback to common paths
            let fallback_paths = [
                "site/config",
                "content/config",
                "config",
                "../../site/config",
                "../../content/config",
                "../../../site/config",
                "../../../content/config",
            ];

            // Find first existing config directory
            let mut found_path = None;
            for path in &fallback_paths {
                if PathBuf::from(path).exists() {
                    found_path = Some(path.to_string());
                    break;
                }
            }

            match found_path {
                Some(path) => {
                    tracing::info!("Using fallback config path: {}", path);
                    path
                }
                None => {
                    tracing::warn!(
                        "No SITE_CONFIG_PATH set and no fallback config directories found"
                    );
                    return None;
                }
            }
        }
    };

    let routes_dir = PathBuf::from(&config_path).join("routes");

    if !routes_dir.exists() || !routes_dir.is_dir() {
        tracing::warn!("Routes directory not found at: {}", routes_dir.display());
        return None;
    }

    let mut all_routes = Vec::new();

    // Read all .toml files in the routes directory
    match fs::read_dir(&routes_dir) {
        Ok(entries) => {
            for entry in entries.flatten() {
                let path = entry.path();

                // Only process .toml files
                if path.extension().and_then(|s| s.to_str()) == Some("toml") {
                    match fs::read_to_string(&path) {
                        Ok(content) => match toml::from_str::<RoutesConfig>(&content) {
                            Ok(config) => {
                                tracing::info!("Loaded routes from: {}", path.display());
                                all_routes.extend(config.routes);
                            }
                            Err(e) => {
                                tracing::error!(
                                    "Failed to parse routes from {}: {}",
                                    path.display(),
                                    e
                                );
                            }
                        },
                        Err(e) => {
                            tracing::error!("Failed to read routes file {}: {}", path.display(), e);
                        }
                    }
                }
            }
        }
        Err(e) => {
            tracing::error!(
                "Failed to read routes directory {}: {}",
                routes_dir.display(),
                e
            );
            return None;
        }
    }

    if all_routes.is_empty() {
        tracing::warn!("No route files found in {}", routes_dir.display());
        return None;
    }

    tracing::info!(
        "Successfully loaded {} routes from {} .toml files",
        all_routes.len(),
        routes_dir.display()
    );
    Some(RoutesConfig { routes: all_routes })
}

/// Get fallback routes configuration for development/testing
fn get_fallback_routes_config() -> &'static RoutesConfig {
    use std::sync::OnceLock;
    static FALLBACK_CONFIG: OnceLock<RoutesConfig> = OnceLock::new();
    FALLBACK_CONFIG.get_or_init(|| RoutesConfig {
        routes: vec![RouteConfigToml {
            path: "/".to_string(),
            component: "HomePage".to_string(),
            title_key: "page-home-title".to_string(),
            language: "en".to_string(),
            enabled: true,
            description_key: Some("page-home-description".to_string()),
            keywords: None,
            priority: Some(1.0),
            menu_group: None,
            menu_order: None,
            menu_icon: None,
            is_external: None,
            requires_auth: None,
            show_in_sitemap: Some(true),
            alternate_paths: None,
            canonical_path: None,
            content_type: None,
            rendering_profile: None,
            template: None,
            props: None,
            unified_component: None,
            lang_prefixes: None,
            params_component: None,
        }],
    })
}

/// Load routes from TOML configuration using dependency injection pattern
///
/// This function provides the core route loading implementation that caches
/// routes from configuration files or fallback sources.
pub fn load_routes_config() -> &'static RoutesConfig {
    ROUTES_CACHE.get_or_init(|| {
        // Try to load from directory using manifest/fallback paths
        #[cfg(all(not(target_arch = "wasm32"), not(has_generated_resources)))]
        {
            if let Some(config) = load_routes_from_directory() {
                return config;
            }
        }

        // Fallback to single route configuration
        tracing::warn!(
            "Unable to load routes from files, using fallback single route configuration"
        );
        get_fallback_routes_config().clone()
    })
}

/// Get the current routes configuration from TOML
pub fn get_routing_config() -> &'static RoutesConfig {
    load_routes_config()
}

// ============================================================================
// ROUTE MAPPINGS (Bidirectional language translation)
// ============================================================================

/// Build route mappings from loaded configuration (dynamic for all languages)
///
/// Returns (forward_mappings, reverse_mappings) where:
/// - forward_mappings: default_language_path → target_language_path
/// - reverse_mappings: target_language_path → default_language_path
pub fn build_route_mappings() -> (
    std::collections::HashMap<String, String>,
    std::collections::HashMap<String, String>,
) {
    use std::collections::HashMap;

    use super::core_types::ROUTE_MAPPINGS_CACHE;

    // Use cached mappings if available
    if let Some(cached) = ROUTE_MAPPINGS_CACHE.get() {
        return cached.clone();
    }

    // Build mappings and cache them
    let mappings = build_route_mappings_uncached();
    let _ = ROUTE_MAPPINGS_CACHE.set(mappings.clone());
    mappings
}

/// Build route mappings without caching (internal implementation)
fn build_route_mappings_uncached() -> (
    std::collections::HashMap<String, String>,
    std::collections::HashMap<String, String>,
) {
    use std::collections::HashMap;

    let config = load_routes_config();
    let language_registry = get_language_registry();
    let available_languages = language_registry.get_available_languages();
    let default_language = language_registry.get_default_language();

    let mut mappings = HashMap::new();
    let mut reverse_mappings = HashMap::new();

    // Group routes by component (path, priority)
    let mut routes_by_component: HashMap<String, HashMap<String, (String, f32)>> = HashMap::new();

    // Build route mappings from configuration
    for route in &config.routes {
        if !route.enabled {
            continue;
        }

        let component_map = routes_by_component
            .entry(route.component.clone())
            .or_default();

        // Prioritize localized routes over /lang/ prefix routes and use priority values
        if let Some((existing_path, existing_priority)) = component_map.get(&route.language) {
            let existing_is_prefix = existing_path.starts_with(&format!("/{}/", route.language));
            let new_is_prefix = route.path.starts_with(&format!("/{}/", route.language));
            let new_priority = route.get_priority();

            // Prioritization logic:
            // 1. Prefer localized routes (non-prefix) over /lang/ prefix routes
            // 2. If both are same type (prefix/non-prefix), prefer higher priority
            let should_replace = if existing_is_prefix && !new_is_prefix {
                true // Always prefer localized over prefix
            } else if !existing_is_prefix && new_is_prefix {
                false // Always keep localized over prefix
            } else {
                // Both are same type, use priority
                new_priority > *existing_priority
            };

            if should_replace {
                component_map.insert(route.language.clone(), (route.path.clone(), new_priority));
            }
        } else {
            component_map.insert(
                route.language.clone(),
                (route.path.clone(), route.get_priority()),
            );
        }
    }

    // Build mappings between languages
    for component_routes in routes_by_component.values() {
        let default_route = component_routes.get(default_language);

        for language in &available_languages {
            if language == default_language {
                continue;
            }

            if let (Some((default_path, _)), Some((lang_path, _))) =
                (default_route, component_routes.get(language))
            {
                // Create bidirectional mapping
                mappings.insert(default_path.clone(), lang_path.clone());
                reverse_mappings.insert(lang_path.clone(), default_path.clone());
            }
        }
    }

    (mappings, reverse_mappings)
}

/// Check if a path is a valid route in the TOML configuration
pub fn is_valid_toml_route(path: &str) -> bool {
    let config = get_routing_config();
    config
        .routes
        .iter()
        .any(|route| route.enabled && route.path == path)
}

/// Get route configuration for a specific path and language
pub fn get_route_for_language(path: &str, language: &str) -> Option<&'static RouteConfigToml> {
    let config = get_routing_config();
    config
        .routes
        .iter()
        .find(|route| route.enabled && route.path == path && route.language == language)
}

/// Get all routes for a specific language
pub fn get_routes_for_language(language: &str) -> Vec<&'static RouteConfigToml> {
    let config = get_routing_config();
    config
        .routes
        .iter()
        .filter(|route| route.enabled && route.language == language)
        .collect()
}

/// Get all enabled routes
pub fn get_all_enabled_routes() -> Vec<&'static RouteConfigToml> {
    let config = get_routing_config();
    config.routes.iter().filter(|route| route.enabled).collect()
}

// build_route_mappings is defined above (lines 175-269) - no duplicate needed

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_routing_config_access() {
        let config = get_routing_config();
        assert!(!config.routes.is_empty());
    }

    #[test]
    fn test_route_validation() {
        // Test with some basic paths that should exist
        let config = get_routing_config();
        let has_home = config.routes.iter().any(|r| r.path == "/" && r.enabled);
        assert!(has_home, "Should have a home route");
    }

    #[test]
    fn test_language_routes() {
        let config = get_routing_config();

        // Skip test if no routes are configured (e.g., in minimal test environments)
        if config.routes.is_empty() {
            println!("Skipping test - no routes configured");
            return;
        }

        let language_registry = rustelo_language::get_language_registry();
        let available_languages = language_registry.get_available_languages();

        // Only check languages that have configured routes
        for language in &available_languages {
            let routes = get_routes_for_language(language);
            if routes.is_empty() {
                println!("No routes configured for language: {language}, skipping assertion");
                continue;
            }
            assert!(
                !routes.is_empty(),
                "Should have routes for language {language}"
            );
        }
    }
}
