//! 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 std::collections::HashMap;
use std::fs;
use std::path::{Path, PathBuf};

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());

    // Ensure path is relative to workspace root
    let resolver = rustelo_utils::PathResolver::new_with_context(rustelo_utils::PathContext::BuildTime)
        .map_err(|e| format!("Failed to create path resolver: {}", e))?;
    let review_pages_dir = resolver
        .workspace_root()
        .join(&devtools_path)
        .join("data/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.enabled {
            let component_name = route.component.clone();

            if !generated_components.contains_key(&component_name) {
                // Check if component already exists
                let component_path = route.component_path.as_deref().unwrap_or("");
                let component_prefix = route.component_prefix.as_deref().unwrap_or("");
                let component_exists = check_component_exists(&component_name, component_path, component_prefix);

                // Respect configuration flags
                let generate_boilerplate = route.generate_boilerplate_only.unwrap_or(false);
                let replace_existing = route.replace_existing_boilerplate.unwrap_or(false);

                let should_generate = match (component_exists, generate_boilerplate, replace_existing) {
                    // Component exists, don't generate unless explicitly requested to replace
                    (true, _, false) => {
                        println!("cargo:warning=📦 Component '{}' exists - skipping generation", component_name);
                        false
                    },
                    // Component exists but replace_existing_boilerplate = true
                    (true, _, true) => {
                        println!("cargo:warning=🔄 Component '{}' exists - replacing due to replace_existing_boilerplate=true", component_name);
                        true
                    },
                    // Component doesn't exist, generate only if requested
                    (false, true, _) => {
                        println!("cargo:warning=🏗️  Component '{}' missing - generating boilerplate", component_name);
                        true
                    },
                    // Component doesn't exist, but generation disabled
                    (false, 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 key_prefix = route
                    .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.page_component.clone(),
                    unified_component: route.get_unified_component(),
                    lang_prefixes: route.get_lang_prefixes(),
                    params_component: route.get_params_component(),
                    props: route.props.clone(),
                    parameter_extraction: route.parameter_extraction.clone(),
                    fallback_component: route.fallback_component.clone(),
                    content_type: route.get_content_type(),

                    // GENERIC COMPONENT CONFIGURATION FIELDS
                    component_dynamic: route.component_dynamic.unwrap_or(false),
                    component_path: route.component_path.as_deref().unwrap_or("").to_string(),
                    component_prefix: route.component_prefix.as_deref().unwrap_or("").to_string(),
                };

                // Insert component info to prevent future duplicate processing
                generated_components.insert(component_name.clone(), info);

                // Only generate if needed
                if should_generate {
                    // TODO: Add cache invalidation logic here for the first time
                    // This would remove the component from cache before generation
                }
            }
        }
    }

    // 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 component_params_setup = generate_params_setup(&component_info.params_component);

    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)
        .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_leptos::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_leptos::content::*;"#;

    let style_mode = component_info
        .params_component
        .get("style_mode")
        .unwrap_or(&"row".to_string())
        .clone();

    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_leptos::content::*;"#;

    let style_mode = component_info
        .params_component
        .get("style_mode")
        .unwrap_or(&"row".to_string())
        .clone();

    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_leptos::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() {
        content.push_str(&format!(
            "pub use {}::{};\n",
            component_info.name.to_lowercase(),
            component_info.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 if a component already exists in the pages source directory
fn check_component_exists(component_name: &str, component_path: &str, component_prefix: &str) -> bool {
    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("."));

    // Build list of possible paths, including hierarchical admin structure
    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 true;
                    }
                }
            } else {
                // For module files, existence is enough
                return true;
            }
        }
    }

    false
}

/// 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)
fn component_to_module_name(component: &str) -> String {
    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
}

/// 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"));
    }
}
