//! Page component generation to replace generate_page! macro
//!
//! This module generates actual page component files using the route configuration
//! and templates, eliminating the need for the generate_page! macro.

use super::route_generation::types::{ComponentInfo, RoutesConfig};
use crate::utils::RouteMetadataAccess;
use std::collections::HashMap;
use std::fs;
use std::path::{Path, PathBuf};

#[derive(Debug, PartialEq)]
enum ComponentLocation {
    VirtualPagesCache(String), // Path in foundation
    Local(String),             // Path in local project
    NotFound,
}

const PAGE_TEMPLATE: &str = r#"//! Auto-generated page component for {component_name}
//! Generated from route configuration with unified component system

use leptos::prelude::*;
use std::collections::HashMap;
use rustelo_core_types::i18n::get_current_language;
use rustelo_core_types::routing::RouteComponent;
{additional_imports}

/// {component_description}
#[component]
pub fn {unified_component}(
    #[prop(optional)] lang: String,
    #[prop(optional)] params: HashMap<String, String>,
) -> impl IntoView {
    let lang = create_memo(move |_| {
        if lang.is_empty() {
            get_current_language().unwrap_or_else(|| "en".to_string())
        } else {
            lang.clone()
        }
    });

    {component_params_setup}

    view! {
        <div class="page-container {css_class}">
            {component_content}
        </div>
    }
}

{additional_components}
"#;

/// Generate page components for all routes
pub fn generate_page_components(
    routes_config: &RoutesConfig,
    output_dir: &str,
) -> Result<(), String> {
    let pages_dir = Path::new(output_dir).join("generated_pages");

    // Clean up old generated files before creating new ones
    clean_generated_directory(&pages_dir)?;

    // Create output directory
    if !pages_dir.exists() {
        fs::create_dir_all(&pages_dir)
            .map_err(|e| format!("Failed to create pages directory: {}", e))?;
    }

    // Also create a development review directory outside target/
    let _workspace_root_str = std::env::var("CARGO_MANIFEST_DIR")
        .map(|p| {
            // Go from crates/shared to workspace root
            Path::new(&p)
                .parent()
                .and_then(|parent| parent.parent())
                .map(|root| root.to_string_lossy().to_string())
                .unwrap_or_else(|| p.clone())
        })
        .unwrap_or_else(|_| ".".to_string());

    let _devtools_path = std::env::var("SITE_DEVTOOLS_PATH")
        .unwrap_or_else(|_| "target/site_build/devtools".to_string());

    // Use manifest resolver to get workspace root
    let resolver = rustelo_utils::ManifestResolver::load()
        .map_err(|e| format!("Failed to create manifest resolver: {}", e))?;
    let review_pages_dir = resolver
        .root()
        .join("site")
        .join("info")
        .join("pages/generated");

    // Clean up old generated files in review directory too
    clean_generated_directory(&review_pages_dir)?;

    if !review_pages_dir.exists() {
        fs::create_dir_all(&review_pages_dir)
            .map_err(|e| format!("Failed to create review pages directory: {}", e))?;
    }

    // Generate components for each unique route component
    let mut generated_components: HashMap<String, ComponentInfo> = HashMap::new();

    for route in &routes_config.routes {
        if route.get_enabled() {
            let component_name = route.component.clone();

            if !generated_components.contains_key(&component_name) {
                // Use route data directly (no generic metadata)
                let component_path = "".to_string(); // Default empty
                let component_prefix = "".to_string(); // Default empty
                let component_location =
                    check_component_location(&component_name, &component_path, &component_prefix);

                // Read configuration flags from route metadata
                let generate_boilerplate = route
                    .get_metadata("generate_boilerplate_only")
                    .map(|s| s.parse().unwrap_or(false))
                    .unwrap_or(false);
                let replace_existing = route
                    .get_metadata("replace_existing_boilerplate")
                    .map(|s| s.parse().unwrap_or(false))
                    .unwrap_or(false);

                let should_generate = match (
                    &component_location,
                    generate_boilerplate,
                    replace_existing,
                ) {
                    // Component exists in Virtual Pages Cache - always skip, no local generation needed
                    (ComponentLocation::VirtualPagesCache(path), _, _) => {
                        let debug_level = rustelo_utils::get_debug_level();
                        if debug_level >= 2 {
                            println!("cargo:warning=📦 Component '{}' exists in Virtual Pages Cache - skipping generation: {}", component_name, path);
                        } else {
                            println!("cargo:warning=📦 Component '{}' exists in Virtual Pages Cache - skipping generation", component_name);
                        }
                        false
                    }
                    // Component exists locally, don't generate unless explicitly requested to replace
                    (ComponentLocation::Local(_), _, false) => {
                        println!(
                            "cargo:warning=📦 Component '{}' exists locally - skipping generation",
                            component_name
                        );
                        false
                    }
                    // Component exists locally but replace_existing_boilerplate = true
                    (ComponentLocation::Local(_), _, true) => {
                        println!("cargo:warning=🔄 Component '{}' exists locally - replacing due to replace_existing_boilerplate=true", component_name);
                        true
                    }
                    // Component doesn't exist, generate only if requested
                    (ComponentLocation::NotFound, true, _) => {
                        println!(
                            "cargo:warning=🏗️  Component '{}' missing - generating boilerplate",
                            component_name
                        );
                        true
                    }
                    // Component doesn't exist, but generation disabled
                    (ComponentLocation::NotFound, false, _) => {
                        println!("cargo:warning=❌ Component '{}' missing but generate_boilerplate_only=false - skipping", component_name);
                        false
                    }
                };

                // Always track components to prevent duplicates, regardless of generation
                let title_key = route.get_title_key().unwrap_or_default();
                let key_prefix = title_key
                    .strip_suffix("-title")
                    .unwrap_or(&format!("{}-page", component_name.to_lowercase()))
                    .to_string();

                let info = ComponentInfo {
                    name: component_name.clone(),
                    key_prefix,
                    page_component: route.get_metadata("page_component"),
                    unified_component: RouteMetadataAccess::get_unified_component(route),
                    lang_prefixes: route
                        .get_metadata("lang_prefixes")
                        .unwrap_or_default()
                        .split(',')
                        .map(|s| s.trim().to_string())
                        .filter(|s| !s.is_empty())
                        .collect(),
                    params_component: route.get_metadata("params_component"),
                    props: route.get_metadata("props"),
                    parameter_extraction: route.get_metadata("parameter_extraction"),
                    fallback_component: route.get_metadata("fallback_component"),
                    content_type: route.content_type.clone(),

                    // GENERIC COMPONENT CONFIGURATION FIELDS
                    component_dynamic: route
                        .get_metadata("component_dynamic")
                        .map(|s| s.parse().unwrap_or(false))
                        .unwrap_or(false),
                    component_path: route.get_metadata("component_path").unwrap_or_default(),
                    component_prefix: route.get_metadata("component_prefix").unwrap_or_default(),
                };

                // Only add to generation list if we should actually generate
                if should_generate {
                    // TODO: Add cache invalidation logic here for the first time
                    // This would remove the component from cache before generation

                    // Insert component info for generation
                    generated_components.insert(component_name.clone(), info);
                }
            }
        }
    }

    // Generate individual component files for both OUT_DIR and review directory
    for (component_name, component_info) in &generated_components {
        generate_component_file(&pages_dir, component_name, component_info)?;
        generate_component_file(&review_pages_dir, component_name, component_info)?;
    }

    // Generate mod.rs file for both directories
    generate_mod_file(&pages_dir, &generated_components)?;
    generate_mod_file(&review_pages_dir, &generated_components)?;

    println!(
        "✅ Generated {} page components in {:?}",
        generated_components.len(),
        pages_dir
    );
    println!("📋 Review copy created in {:?}", review_pages_dir);

    Ok(())
}

/// Generate a single component file
fn generate_component_file(
    pages_dir: &Path,
    component_name: &str,
    component_info: &ComponentInfo,
) -> Result<(), String> {
    let file_name = format!("{}.rs", component_name.to_lowercase());
    let file_path = pages_dir.join(&file_name);

    // Determine component type and generate appropriate content
    let (additional_imports, component_content, css_class) = match component_name {
        "ContentPost" => generate_content_post_component(component_info),
        "ContentCategory" => generate_content_category_component(component_info),
        "ContentIndexPage" | "ContentIndex" => generate_content_index_component(component_info),
        _ => generate_standard_component(component_info),
    };

    // Generate component parameters setup
    let empty_params = HashMap::new();
    let component_params_setup = generate_params_setup(&empty_params);

    let component_description = format!(
        "{} page component - {}",
        component_name, component_info.key_prefix
    );

    let content = PAGE_TEMPLATE
        .replace("{component_name}", component_name)
        .replace(
            "{unified_component}",
            component_info
                .unified_component
                .as_deref()
                .unwrap_or("NotFound"),
        )
        .replace("{component_description}", &component_description)
        .replace("{additional_imports}", &additional_imports)
        .replace("{component_params_setup}", &component_params_setup)
        .replace("{component_content}", &component_content)
        .replace("{css_class}", &css_class)
        .replace("{additional_components}", "");

    fs::write(&file_path, content)
        .map_err(|e| format!("Failed to write component file {}: {}", file_name, e))?;

    println!("📄 Generated component: {}", file_name);
    Ok(())
}

/// Generate parameters setup code
fn generate_params_setup(params_component: &HashMap<String, String>) -> String {
    if params_component.is_empty() {
        "    // No component parameters configured".to_string()
    } else {
        let mut setup_lines =
            vec!["    // Component parameters from route configuration".to_string()];

        for (key, value) in params_component {
            setup_lines.push(format!(
                "    let {} = \"{}\".to_string();",
                key.replace("-", "_"),
                value
            ));
        }

        setup_lines.join("\n")
    }
}

/// Generate content post component
fn generate_content_post_component(component_info: &ComponentInfo) -> (String, String, String) {
    let imports = r#"
use rustelo_core_types::content::*;
use rustelo_components::content::*;
use rustelo_pages::post_viewer::unified::PostViewerPage;"#;

    let content = format!(
        r#"
            <PostViewerPage
                _language={{lang.get()}}
                content_type="{}".to_string()
                slug={{params.get("slug").cloned().unwrap_or_default()}}
            />
"#,
        component_info.content_type.as_deref().unwrap_or("blog")
    );

    (imports.to_string(), content, "post-viewer-page".to_string())
}

/// Generate content category component
fn generate_content_category_component(component_info: &ComponentInfo) -> (String, String, String) {
    let imports = r#"
use rustelo_core_types::content::*;
use rustelo_components::content::*;"#;

    let style_mode = "row".to_string(); // Default style_mode since params_component is Option<String>

    let content = format!(
        r#"
            <CategoryView
                lang={{lang.get()}}
                content_type="{}"
                style_mode="{}"
                params={{params}}
            />
"#,
        component_info.content_type.as_deref().unwrap_or("blog"),
        style_mode
    );

    (imports.to_string(), content, "category-page".to_string())
}

/// Generate content index component
fn generate_content_index_component(component_info: &ComponentInfo) -> (String, String, String) {
    let imports = r#"
use rustelo_core_types::content::*;
use rustelo_components::content::*;"#;

    let style_mode = "row".to_string(); // Default style_mode since params_component is Option<String>

    let content = format!(
        r#"
            <ContentIndex
                lang={{lang.get()}}
                content_type="{}"
                style_mode="{}"
                params={{params}}
            />
"#,
        component_info.content_type.as_deref().unwrap_or("blog"),
        style_mode
    );

    (
        imports.to_string(),
        content,
        "content-index-page".to_string(),
    )
}

/// Generate standard component
fn generate_standard_component(component_info: &ComponentInfo) -> (String, String, String) {
    let imports = "use rustelo_components::rustelo_pages::*;";

    let content = format!(
        r#"
            <div class="page-content">
                <h1>"TODO: Add title translation for {}"</h1>
                <p>"TODO: Add description translation for {}"</p>

                // TODO: Implement specific component logic
                <div class="placeholder-content">
                    "Page content for {} goes here"
                </div>
            </div>
"#,
        component_info.key_prefix, component_info.key_prefix, component_info.name
    );

    (
        imports.to_string(),
        content,
        format!("{}-page", component_info.name.to_lowercase()),
    )
}

/// Generate mod.rs file that exports all generated components
fn generate_mod_file(
    pages_dir: &Path,
    components: &HashMap<String, ComponentInfo>,
) -> Result<(), String> {
    let mod_path = pages_dir.join("mod.rs");

    let mut content = String::from("//! Auto-generated page components module\n\n");

    // Add module declarations
    for component_name in components.keys() {
        let module_name = component_name.to_lowercase();
        content.push_str(&format!("pub mod {};\n", module_name));
        content.push_str(&format!("pub use {}::*;\n", module_name));
    }

    // Add re-export convenience
    content.push_str("\n// Re-export all unified components for easy access\n");
    for component_info in components.values() {
        if let Some(unified_component) = &component_info.unified_component {
            content.push_str(&format!(
                "pub use {}::{};\n",
                component_info.name.to_lowercase(),
                unified_component
            ));
        }
    }

    fs::write(&mod_path, content).map_err(|e| format!("Failed to write mod.rs: {}", e))?;

    println!("📄 Generated mod.rs with {} exports", components.len());
    Ok(())
}

/// Check component location: Virtual Pages Cache (foundation), local, or not found
fn check_component_location(
    component_name: &str,
    component_path: &str,
    component_prefix: &str,
) -> ComponentLocation {
    let module_name = component_to_module_name(component_name);

    // Get the workspace root dynamically
    let workspace_root = std::env::var("CARGO_MANIFEST_DIR")
        .map(|p| {
            let mut path = PathBuf::from(p);
            // Navigate up from tools crate to workspace root
            while !path.join("Cargo.toml").exists() || !path.join("crates").exists() {
                if !path.pop() {
                    break;
                }
            }
            path
        })
        .unwrap_or_else(|_| PathBuf::from("."));

    // FIRST: Check Virtual Pages Cache (foundation components) using PAP-compliant dynamic discovery
    // Navigate to foundation from current workspace
    let mut foundation_root = workspace_root.clone();
    foundation_root.pop(); // Go up from website-impl
    foundation_root.pop(); // Go up from website
    foundation_root.push("rustelo/crates/foundation/crates");

    // Use dynamic discovery instead of hardcoded paths
    if let Some(discovered_path) =
        discover_component_in_foundation(component_name, &foundation_root)
    {
        return ComponentLocation::VirtualPagesCache(discovered_path);
    }

    // SECOND: Check local override paths
    let mut possible_paths = vec![
        workspace_root.join(format!("crates/pages/src/{}/mod.rs", module_name)),
        workspace_root.join(format!("crates/pages/src/{}/unified.rs", module_name)),
        workspace_root.join("crates/pages/src/lib.rs"),
    ];

    // Use component_path and component_prefix for fully flexible component location
    if !component_path.is_empty() {
        let module_path = if !component_prefix.is_empty() {
            component_name
                .strip_prefix(component_prefix)
                .unwrap_or(component_name)
        } else {
            component_name
        }
        .to_lowercase();

        // Add hierarchical component paths
        possible_paths.push(workspace_root.join(format!(
            "crates/pages/src/{}/{}/mod.rs",
            component_path, module_path
        )));
        possible_paths.push(workspace_root.join(format!(
            "crates/pages/src/{}/{}/unified.rs",
            component_path, module_path
        )));
    }

    // Try both the base component name and with "Page" suffix
    let component_names_to_check = vec![
        component_name.to_string(),
        format!("{}Page", component_name), // Check for generated "Page" suffix version
    ];

    for path in &possible_paths {
        if path.exists() {
            // For lib.rs, check if any of the component variations is exported
            if path.file_name().and_then(|n| n.to_str()) == Some("lib.rs") {
                for name_variant in &component_names_to_check {
                    if component_is_exported(name_variant, path) {
                        return ComponentLocation::Local(path.to_string_lossy().to_string());
                    }
                }
            } else {
                // For module files, existence is enough
                return ComponentLocation::Local(path.to_string_lossy().to_string());
            }
        }
    }

    ComponentLocation::NotFound
}

/// Check if a component is exported from a given file
fn component_is_exported(component_name: &str, file_path: &Path) -> bool {
    if let Ok(content) = fs::read_to_string(file_path) {
        // Check for various export patterns
        content.contains(&format!("pub use {}::", component_name))
            || content.contains(&format!("pub use.*{}.*", component_name))
            || content.contains(&format!("pub fn {}(", component_name))
            || content.contains(&format!("pub use {}", component_name))
    } else {
        false
    }
}

/// Convert component name to module name (PascalCase -> snake_case)
/// PAP-compliant: Uses dynamic discovery instead of hardcoded mappings
fn component_to_module_name(component: &str) -> String {
    // Standard snake_case conversion
    let mut result = String::new();
    let mut chars = component.chars();

    if let Some(first) = chars.next() {
        result.push(first.to_ascii_lowercase());
    }

    for ch in chars {
        if ch.is_ascii_uppercase() {
            result.push('_');
            result.push(ch.to_ascii_lowercase());
        } else {
            result.push(ch);
        }
    }

    result
}

/// PAP-compliant: Dynamically discover component paths in foundation
/// This eliminates the need for hardcoded mappings
fn discover_component_in_foundation(
    component_name: &str,
    foundation_root: &Path,
) -> Option<String> {
    let foundation_sources = [
        foundation_root.join("rustelo_pages/src"),
        foundation_root.join("rustelo_components/src"),
    ];

    // Check for various naming patterns dynamically
    let mut search_patterns = vec![
        component_name.to_lowercase(),
        component_to_module_name(component_name),
        format!("{}_link", component_name.to_lowercase()), // For External -> external_link
    ];

    // Handle plural to singular conversions for AdminUsers -> admin/user
    let snake_case = component_to_module_name(component_name);
    if let Some(underscore_pos) = snake_case.find('_') {
        let (prefix, suffix) = snake_case.split_at(underscore_pos);
        let suffix = &suffix[1..]; // Remove underscore

        // Try singular version: admin_users -> admin_user
        if suffix.ends_with("s") && suffix.len() > 1 {
            let singular_suffix = &suffix[..suffix.len() - 1];
            search_patterns.push(format!("{}_{}", prefix, singular_suffix));
        }
    }

    for source_dir in &foundation_sources {
        if source_dir.exists() {
            // Search recursively for component files
            if let Ok(discovered_path) =
                find_component_recursively(source_dir, component_name, &search_patterns)
            {
                return Some(discovered_path);
            }
        }
    }

    None
}

/// Recursively search for component in directory structure
fn find_component_recursively(
    search_dir: &Path,
    _component_name: &str,
    search_patterns: &[String],
) -> Result<String, ()> {
    if let Ok(entries) = std::fs::read_dir(search_dir) {
        for entry in entries.flatten() {
            let path = entry.path();

            if path.is_file() {
                // Check if this file contains the component
                if let Some(file_stem) = path.file_stem().and_then(|s| s.to_str()) {
                    // Check if any search pattern matches the file stem
                    for pattern in search_patterns {
                        if file_stem == pattern {
                            return Ok(file_stem.to_string());
                        }
                    }
                }
            } else if path.is_dir() {
                let dir_name = path.file_name().and_then(|s| s.to_str()).unwrap_or("");

                // Check if directory contains component modules
                for pattern in search_patterns {
                    // Check for hierarchical patterns like admin/user, content/index
                    if let Some(underscore_pos) = pattern.find('_') {
                        let (prefix, suffix) = pattern.split_at(underscore_pos);
                        let suffix = &suffix[1..]; // Remove underscore

                        if dir_name == prefix {
                            let sub_module_path = path.join(suffix);
                            if sub_module_path.join("mod.rs").exists()
                                || sub_module_path.join("unified.rs").exists()
                            {
                                return Ok(format!("{}/{}", prefix, suffix));
                            }
                        }
                    }

                    // Check single-level directory
                    if dir_name == pattern
                        && (path.join("mod.rs").exists() || path.join("unified.rs").exists())
                    {
                        return Ok(dir_name.to_string());
                    }
                }

                // Recurse into subdirectories
                if let Ok(found) =
                    find_component_recursively(&path, _component_name, search_patterns)
                {
                    return Ok(if dir_name.is_empty() {
                        found
                    } else {
                        format!("{}/{}", dir_name, found)
                    });
                }
            }
        }
    }

    Err(())
}

/// Clean up old generated files in the specified directory
fn clean_generated_directory(dir: &Path) -> Result<(), String> {
    if dir.exists() {
        println!("🧹 Cleaning old generated files from {:?}", dir);

        // Read directory contents
        let entries =
            fs::read_dir(dir).map_err(|e| format!("Failed to read directory {:?}: {}", dir, e))?;

        let mut files_removed = 0;
        for entry in entries {
            let entry = entry.map_err(|e| format!("Failed to read directory entry: {}", e))?;
            let path = entry.path();

            if path.is_file() {
                // Only remove .rs files except mod.rs, keep other files like .gitignore
                if let Some(extension) = path.extension() {
                    if extension == "rs" {
                        if let Some(filename) = path.file_name().and_then(|n| n.to_str()) {
                            if filename != "mod.rs" {
                                fs::remove_file(&path).map_err(|e| {
                                    format!("Failed to remove file {:?}: {}", path, e)
                                })?;
                                files_removed += 1;
                            }
                        }
                    }
                }
            }
        }

        if files_removed > 0 {
            println!("🗑️  Removed {} old generated files", files_removed);
        } else {
            println!("✨ No old generated files to remove");
        }
    } else {
        println!("📁 Directory {:?} doesn't exist, no cleanup needed", dir);
    }

    Ok(())
}

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

    #[test]
    fn test_params_setup_generation() {
        let mut params = HashMap::new();
        params.insert("content-type".to_string(), "blog".to_string());
        params.insert("style-mode".to_string(), "grid".to_string());

        let setup = generate_params_setup(&params);
        assert!(setup.contains("content_type"));
        assert!(setup.contains("style_mode"));
        assert!(setup.contains("blog"));
        assert!(setup.contains("grid"));
    }

    #[test]
    fn test_params_setup_empty() {
        let params = HashMap::new();
        let setup = generate_params_setup(&params);
        assert!(setup.contains("No component parameters"));
    }
}
