//! Import Generation for Missing Pages and Components
//!
//! This module analyzes routes and generates imports for missing pages and
//! their component dependencies from the Rustelo foundation.

use std::collections::{HashMap, HashSet};
use std::fs;
use std::path::{Path, PathBuf};

use crate::build::path_resolution::init_build_environment;

/// Information about a required page from routes
#[derive(Debug, Clone)]
pub struct PageRequirement {
    pub component: String,
    pub page_component: String,
    pub module_path: Option<String>,
}

/// Information about a required component
#[derive(Debug, Clone)]
pub struct ComponentRequirement {
    pub name: String,
    pub module_path: String,
}

/// Generate import statements for missing pages and components
pub fn generate_missing_imports(
    workspace_root: &Path,
) -> Result<(String, String), Box<dyn std::error::Error>> {
    init_build_environment();

    // 1. Load routes configuration
    let required_pages = load_required_pages(workspace_root)?;

    // 2. Scan existing local pages
    let local_pages = scan_local_pages(workspace_root)?;

    // 3. Determine missing pages
    let missing_pages: Vec<_> = required_pages
        .into_iter()
        .filter(|page| !local_pages.contains(&page.component))
        .collect();

    // 4. Generate page imports
    let page_imports = generate_page_imports(&missing_pages);

    // 5. Analyze component dependencies
    let required_components = analyze_component_dependencies(&missing_pages)?;

    // 6. Generate component imports
    let component_imports = generate_component_imports(&required_components);

    Ok((page_imports, component_imports))
}

/// Load required pages from routes configuration
fn load_required_pages(
    _workspace_root: &Path,
) -> Result<Vec<PageRequirement>, Box<dyn std::error::Error>> {
    let content_path = crate::build::path_resolution::get_content_path();
    let routes_dir = content_path.join("config").join("routes");

    if !routes_dir.exists() {
        return Ok(Vec::new());
    }

    let mut requirements = Vec::new();

    // Read all route files (en.toml, es.toml, etc.)
    for entry in fs::read_dir(&routes_dir)? {
        let entry = entry?;
        if entry.path().extension().is_some_and(|ext| ext == "toml") {
            let route_content = fs::read_to_string(entry.path())?;
            let routes: toml::Value = toml::from_str(&route_content)?;

            if let Some(routes_array) = routes.get("routes").and_then(|v| v.as_array()) {
                for route in routes_array {
                    if let Some(component) = route.get("component").and_then(|v| v.as_str()) {
                        let page_component = route
                            .get("page_component")
                            .and_then(|v| v.as_str())
                            .unwrap_or(&format!("{}Page", component))
                            .to_string();

                        let module_path = route
                            .get("module_path")
                            .and_then(|v| v.as_str())
                            .map(|s| s.to_string());

                        requirements.push(PageRequirement {
                            component: component.to_string(),
                            page_component,
                            module_path,
                        });
                    }
                }
            }
        }
    }

    // Remove duplicates
    requirements.sort_by(|a, b| a.component.cmp(&b.component));
    requirements.dedup_by(|a, b| a.component == b.component);

    Ok(requirements)
}

/// Scan for existing local pages in the implementation
fn scan_local_pages(workspace_root: &Path) -> Result<HashSet<String>, Box<dyn std::error::Error>> {
    let pages_dir = workspace_root.join("src").join("pages");
    let mut local_pages = HashSet::new();

    if !pages_dir.exists() {
        return Ok(local_pages);
    }

    // Scan for .rs files and directories
    for entry in fs::read_dir(&pages_dir)? {
        let entry = entry?;
        let path = entry.path();

        if path.is_file() && path.extension().is_some_and(|ext| ext == "rs") {
            if let Some(name) = path.file_stem().and_then(|n| n.to_str()) {
                if name != "mod" {
                    // Convert snake_case to PascalCase (e.g., work_request -> WorkRequest)
                    let component_name = snake_to_pascal(name);
                    local_pages.insert(component_name);
                }
            }
        } else if path.is_dir() {
            if let Some(name) = path.file_name().and_then(|n| n.to_str()) {
                let component_name = snake_to_pascal(name);
                local_pages.insert(component_name);
            }
        }
    }

    Ok(local_pages)
}

/// Convert snake_case to PascalCase
fn snake_to_pascal(snake_str: &str) -> String {
    snake_str
        .split('_')
        .map(|word| {
            let mut chars: Vec<char> = word.chars().collect();
            if !chars.is_empty() {
                chars[0] = chars[0].to_uppercase().next().unwrap_or(chars[0]);
            }
            chars.into_iter().collect::<String>()
        })
        .collect()
}

/// Generate import statements for missing pages
fn generate_page_imports(missing_pages: &[PageRequirement]) -> String {
    let mut imports = String::new();
    imports.push_str("// Auto-generated page imports from Rustelo foundation\n");
    imports.push_str("// This file is generated by the build system - do not edit manually\n\n");

    for page in missing_pages {
        let module_path = if let Some(ref path) = page.module_path {
            format!("rustelo_pages_leptos::{}::{}", path, page.page_component)
        } else {
            let snake_case = pascal_to_snake(&page.component);
            format!(
                "rustelo_pages_leptos::{}::{}",
                snake_case, page.page_component
            )
        };

        imports.push_str(&format!("pub use {} as {};\n", module_path, page.component));
    }

    imports
}

/// Convert PascalCase to snake_case
fn pascal_to_snake(pascal_str: &str) -> String {
    let mut result = String::new();
    let chars = pascal_str.chars();

    for ch in chars {
        if ch.is_uppercase() && !result.is_empty() {
            result.push('_');
        }
        result.push(ch.to_lowercase().next().unwrap_or(ch));
    }

    result
}

/// Analyze component dependencies of missing pages
fn analyze_component_dependencies(
    _missing_pages: &[PageRequirement],
) -> Result<Vec<ComponentRequirement>, Box<dyn std::error::Error>> {
    // For now, include common components that are typically used by foundation
    // pages In the future, this could parse the actual foundation page source
    // files

    // Don't generate any automatic component imports for now
    // Let the local implementation define what components it needs
    let common_components = vec![];

    Ok(common_components)
}

/// Generate import statements for required components
fn generate_component_imports(required_components: &[ComponentRequirement]) -> String {
    let mut imports = String::new();
    imports.push_str("// Auto-generated component imports from Rustelo foundation\n");
    imports.push_str("// This file is generated by the build system - do not edit manually\n\n");

    for component in required_components {
        imports.push_str(&format!("pub use {};\n", component.module_path));
    }

    imports
}

/// Generate simplified AppRouter component that works with current foundation
fn generate_client_router(_required_pages: &[PageRequirement]) -> String {
    let mut router = String::new();
    router.push_str("// Auto-generated simple AppRouter using basic routing\n");
    router.push_str("// This file is generated by the build system - do not edit manually\n\n");

    router.push_str("use leptos::prelude::*;\n\n");

    router.push_str("/// Simple App router component\n");
    router.push_str("#[component]\n");
    router.push_str("pub fn AppRouter() -> impl IntoView {\n");
    router.push_str("    // Get current path from browser\n");
    router.push_str("    let current_path = get_current_path();\n");
    router.push_str("    \n");
    router.push_str("    view! {\n");
    router.push_str("        <div class=\"rustelo-app min-h-screen bg-gray-50\">\n");
    router.push_str("            <main class=\"container mx-auto py-8\">\n");
    router.push_str("                {move || render_page_for_path(&current_path)}\n");
    router.push_str("            </main>\n");
    router.push_str("        </div>\n");
    router.push_str("    }\n");
    router.push_str("}\n\n");

    router.push_str("/// Get current path from browser\n");
    router.push_str("fn get_current_path() -> String {\n");
    router.push_str("    #[cfg(target_arch = \"wasm32\")]\n");
    router.push_str("    {\n");
    router.push_str("        if let Some(window) = web_sys::window() {\n");
    router.push_str("            if let Ok(location) = window.location() {\n");
    router.push_str(
        "                return location.pathname().unwrap_or_else(|_| \"/\".to_string());\n",
    );
    router.push_str("            }\n");
    router.push_str("        }\n");
    router.push_str("    }\n");
    router.push_str("    \"/\".to_string()\n");
    router.push_str("}\n\n");

    router.push_str("/// Render page component based on current path\n");
    router.push_str("fn render_page_for_path(path: &str) -> impl IntoView {\n");
    router.push_str("    match path {\n");

    // Load routes from config and generate simple match arms
    let content_path = crate::build::path_resolution::get_content_path();
    let routes_dir = content_path.join("config").join("routes");

    if routes_dir.exists() {
        if let Ok(entries) = fs::read_dir(&routes_dir) {
            for entry in entries.flatten() {
                let path = entry.path();
                if path.extension().is_some_and(|ext| ext == "toml") {
                    if let Ok(route_content) = fs::read_to_string(&path) {
                        if let Ok(routes) = toml::from_str::<toml::Value>(&route_content) {
                            if let Some(routes_array) =
                                routes.get("routes").and_then(|v| v.as_array())
                            {
                                for route in routes_array {
                                    if let (Some(route_path), Some(component)) = (
                                        route.get("path").and_then(|v| v.as_str()),
                                        route.get("component").and_then(|v| v.as_str()),
                                    ) {
                                        router.push_str(&format!(
                                            "        \"{}\" => view! {{ <div class=\"page-{}\">\n",
                                            route_path,
                                            component.to_lowercase()
                                        ));
                                        router.push_str(&format!(
                                            "            <h1>\"{}\"</h1>\n",
                                            component
                                        ));
                                        router.push_str(
                                            "            <p>\"This page is working with basic \
                                             routing.\"</p>\n",
                                        );
                                        router.push_str("        </div> }.into_any(),\n");
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    router.push_str("        _ => view! { <div class=\"not-found\">\n");
    router.push_str("            <h1>\"404 - Page Not Found\"</h1>\n");
    router.push_str("            <p>\"The page you're looking for doesn't exist.\"</p>\n");
    router.push_str("        </div> }.into_any(),\n");
    router.push_str("    }\n");
    router.push_str("}\n");

    router
}

/// Write import files to the OUT_DIR
pub fn write_import_files(
    workspace_root: &Path,
    out_dir: &str,
) -> Result<(), Box<dyn std::error::Error>> {
    let (page_imports, component_imports) = generate_missing_imports(workspace_root)?;
    let required_pages = load_required_pages(workspace_root)?;

    // Write page imports
    let pages_file = Path::new(out_dir).join("pages_imports.rs");
    fs::write(&pages_file, page_imports)?;

    // Write component imports
    let components_file = Path::new(out_dir).join("components_imports.rs");
    fs::write(&components_file, component_imports)?;

    // Write generated router
    let router_code = generate_client_router(&required_pages);
    let router_file = Path::new(out_dir).join("app_router.rs");
    fs::write(&router_file, router_code)?;

    println!(
        "cargo:warning=Generated import files: {:?}, {:?}, {:?}",
        pages_file, components_file, router_file
    );

    Ok(())
}

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

    #[test]
    fn test_snake_to_pascal() {
        assert_eq!(snake_to_pascal("work_request"), "WorkRequest");
        assert_eq!(snake_to_pascal("home"), "Home");
        assert_eq!(snake_to_pascal("not_found"), "NotFound");
    }

    #[test]
    fn test_pascal_to_snake() {
        assert_eq!(pascal_to_snake("WorkRequest"), "work_request");
        assert_eq!(pascal_to_snake("Home"), "home");
        assert_eq!(pascal_to_snake("NotFound"), "not_found");
    }
}
