//! Shared build script - Using Provider pattern with Rustelo foundation traits
//!
//! This build script uses the Provider pattern with dependency injection
//! instead of direct function calls. Follows PAP principles completely.
//!
//! Includes build-time route validation to ensure menu routes match configured pages.

/// Walk up from `CARGO_MANIFEST_DIR` to find the Cargo workspace root.
///
/// Falls back to `CARGO_MANIFEST_DIR` itself when no ancestor contains `[workspace]`,
/// which is correct for single-crate projects.
fn find_workspace_root() -> Result<std::path::PathBuf, Box<dyn std::error::Error>> {
    let pkg_dir = std::env::var("CARGO_MANIFEST_DIR")
        .map_err(|_| "CARGO_MANIFEST_DIR not set — this function is only valid in build scripts")?;
    let mut current = std::path::PathBuf::from(pkg_dir);

    for _ in 0..15 {
        let cargo_toml = current.join("Cargo.toml");
        if cargo_toml.exists() {
            if let Ok(content) = std::fs::read_to_string(&cargo_toml) {
                if content.contains("[workspace]") {
                    return Ok(current);
                }
            }
        }
        current = current
            .parent()
            .ok_or("Reached filesystem root without finding a [workspace] Cargo.toml")?
            .to_path_buf();
    }

    Err("Workspace root not found — ensure a Cargo.toml with [workspace] exists in the project hierarchy".into())
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Use new PAP-compliant manifest system
    // let _manifest = rustelo_utils::get_manifest();
    let content_path = rustelo_utils::manifest_content_path();
    let config_path = rustelo_utils::manifest_config_path();
    let routes_path = rustelo_utils::routes_path();
    let ui_path = rustelo_utils::ui_path();

    println!("cargo:rerun-if-changed={}", routes_path.display());
    println!("cargo:rerun-if-changed={}", config_path.display());
    println!("cargo:rerun-if-changed={}", content_path.display());
    println!("cargo:rerun-if-changed={}", ui_path.display());

    // Trigger rebuild when unified site config changes.
    // ManifestResolver is preferred; if it fails (no rustelo.manifest.toml), derive from CARGO_MANIFEST_DIR.
    let manifest_dir = rustelo_utils::ManifestResolver::load()
        .map(|m| m.root().to_path_buf())
        .or_else(|_| find_workspace_root())?;

    let site_ncl = manifest_dir.join("site").join("config").join("index.ncl");
    if site_ncl.exists() {
        println!("cargo:rerun-if-changed={}", site_ncl.display());
    }

    // Generate shared route constants
    generate_shared_routes()?;

    // Validate menu routes match configured routes
    validate_menu_routes(&routes_path, &ui_path)?;

    println!("cargo:warning=Shared build completed");

    Ok(())
}

fn generate_shared_routes() -> Result<(), Box<dyn std::error::Error>> {
    use std::collections::HashMap;
    use std::path::Path;
    use std::{env, fs};

    let mut all_routes: HashMap<String, HashMap<String, String>> = HashMap::new();

    let manifest_dir = rustelo_utils::ManifestResolver::load()
        .map(|m| m.root().to_path_buf())
        .or_else(|_| find_workspace_root())
        .expect("Cannot determine workspace root — check CARGO_MANIFEST_DIR or add rustelo.manifest.toml");

    let site_cfg = build_config::site_config::load_unified_site_config(&manifest_dir)
        .unwrap_or_else(|e| {
            panic!(
                "Cannot load site configuration: {e}\n\
             Install Nickel: cargo install nickel-lang-cli"
            )
        });

    println!(
        "cargo:warning=Shared: Loaded site configuration ({} languages)",
        site_cfg.languages().len()
    );
    for (lang, routes) in site_cfg.routes_expanded() {
        let mut lang_routes = HashMap::new();
        for route in routes {
            lang_routes.insert(route.component.to_lowercase(), route.path.clone());
        }
        all_routes.insert(lang, lang_routes);
    }

    // Generate Rust code for route constants
    let mut generated_code = String::new();
    generated_code.push_str("// Auto-generated route constants\n");
    generated_code.push_str("// Generated from manifest routes\n\n");
    generated_code.push_str("pub mod routes {\n");

    for (lang, routes) in &all_routes {
        generated_code.push_str(&format!("    pub mod {} {{\n", lang));
        for (route_name, path) in routes {
            let const_name = route_name.to_uppercase();
            generated_code.push_str(&format!(
                "        pub const {}: &str = \"{}\";\n",
                const_name, path
            ));
        }
        generated_code.push_str("    }\n\n");
    }

    generated_code.push_str("}\n");

    // Write the generated routes
    let out_dir = env::var("OUT_DIR")?;
    let routes_file = Path::new(&out_dir).join("routes_generated.rs");
    fs::write(routes_file, generated_code)?;

    Ok(())
}

/// Validate that all menu routes point to configured pages
fn validate_menu_routes(
    routes_path: &std::path::Path,
    ui_path: &std::path::Path,
) -> Result<(), Box<dyn std::error::Error>> {
    use std::collections::{HashMap, HashSet};
    use std::fs;

    let menus_path = ui_path.join("menus");

    // If menus directory doesn't exist, skip validation
    if !menus_path.exists() {
        return Ok(());
    }

    // Load all configured routes by language using typed loading
    let mut configured_routes: HashMap<String, HashSet<String>> = HashMap::new();

    if routes_path.exists() {
        // Use typed route loading (NCL-first, TOML-fallback)
        match build_config::route_config::load_all_routes(routes_path) {
            Ok(routes_by_lang) => {
                for (lang, routes) in routes_by_lang {
                    let mut lang_routes = HashSet::new();
                    for route in routes {
                        // Collect all route paths for validation
                        lang_routes.insert(route.path.clone());
                    }
                    configured_routes.insert(lang, lang_routes);
                }
            }
            Err(e) => {
                println!(
                    "cargo:warning=Failed to load routes for menu validation: {}",
                    e
                );
                // Continue without validation if routes can't be loaded
                return Ok(());
            }
        }
    }

    // Create route validator
    let validator = rustelo_utils::RouteValidator::new(configured_routes);

    // Validate all menu files
    for entry in fs::read_dir(&menus_path)? {
        let entry = entry?;
        if entry.path().extension().is_some_and(|ext| ext == "toml") {
            let path = entry.path();
            if let Some(language) = path.file_stem().and_then(|s| s.to_str()) {
                let menu_content = fs::read_to_string(&path)?;
                let menu: toml::Value = toml::from_str(&menu_content)?;

                if let Some(menu_items) = menu.get("menu").and_then(|v| v.as_array()) {
                    let mut route_checks = Vec::new();

                    for menu_item in menu_items {
                        if let Some(route) = menu_item.get("route").and_then(|v| v.as_str()) {
                            let is_external = menu_item
                                .get("is_external")
                                .and_then(|v| v.as_bool())
                                .unwrap_or(false);

                            route_checks.push((
                                route.to_string(),
                                language.to_string(),
                                is_external,
                            ));
                        }
                    }

                    // Validate batch
                    if let Err(errors) = validator.validate_batch(&route_checks) {
                        let error_messages = errors
                            .iter()
                            .map(|e| format!("  - {}", e))
                            .collect::<Vec<_>>()
                            .join("\n");

                        println!(
                            "cargo:warning=Menu validation errors in {}:\n{}",
                            path.display(),
                            error_messages
                        );
                    }
                }
            }
        }
    }

    Ok(())
}
