// Page generation system for pages crate
// Uses route configuration to generate proper Client/SSR components

use serde::{Deserialize, Serialize};
use std::env;
use std::fs;
use std::path::Path;

/// Route configuration structure
#[derive(Deserialize, Serialize, Debug)]
pub struct RouteConfig {
    pub path: String,
    pub page_component: String,
    pub language: String,
    pub enabled: bool,
    #[serde(default)]
    pub unified_component: Option<String>,
    #[serde(default)]
    pub lang_prefixes: Option<Vec<String>>,
    #[serde(default)]
    pub props: Option<toml::Value>,
    #[serde(default)]
    pub content_type: Option<String>,
    #[serde(default)]
    pub module_path: Option<String>,
}

/// Routes configuration container
#[derive(Deserialize, Serialize, Debug)]
pub struct RoutesConfig {
    pub routes: Vec<RouteConfig>,
}

/// Component generation information
#[derive(Debug, Clone, Serialize)]
pub struct ComponentInfo {
    pub component_name: String,
    pub unified_component: String,
    pub lang_prefixes: Vec<String>,
    pub props: Option<toml::Value>,
    pub has_existing_unified: bool,
    pub is_content_page: bool,
    pub _module_path: String,
}

/// Generate page components using smart cache system for incremental builds
pub fn generate_page_components() -> Result<(), Box<dyn std::error::Error>> {
    use rustelo_tools::{hash_route_config, is_cache_available, CacheStatus, SmartCache};

    let out_dir = env::var("OUT_DIR")?;
    let out_path = Path::new(&out_dir);

    // Skip caching if SITE_DEVTOOLS_PATH not available, use traditional generation
    if !is_cache_available() {
        println!(
            "cargo:warning=SITE_DEVTOOLS_PATH not set - using direct generation without cache"
        );
        return generate_components_direct(&out_path);
    }

    // Initialize smart cache for pages
    let cache = SmartCache::new("pages")?;

    // Load route configurations
    let components = load_route_configurations()?;

    // Debug: show component counts
    let total_components = components.len();
    let generate_count = components.iter().filter(|c| c.has_existing_unified).count();
    let skip_count = total_components - generate_count;
    println!(
        "cargo:warning=Components: {} total, {} to generate, {} skipped",
        total_components, generate_count, skip_count
    );

    println!("cargo:warning=Using smart cache for incremental page generation");

    for component_info in components {
        if component_info.has_existing_unified {
            let filename = format!("page_{}.rs", component_info.component_name);

            // Calculate hash for this specific component's config
            let component_route_content = serde_json::to_string(&component_info)?;
            let dependencies = vec![
                get_route_config_path()?,
                // Add other dependencies that affect this component
            ];
            let source_hash = hash_route_config(&component_route_content, &dependencies);

            // Check cache status
            match cache.check_file_cache(&filename, &source_hash)? {
                CacheStatus::Fresh(_) => {
                    // Use cached version - fast path!
                    cache.copy_from_cache(&filename, &out_path)?;
                    println!("cargo:warning=Cached: {}", filename);
                }
                CacheStatus::Stale(_) | CacheStatus::Missing(_) => {
                    // Generate new component - slow path
                    let content = generate_single_component_content(&component_info)?;
                    cache.update_cache(
                        &filename,
                        &content,
                        &source_hash,
                        &out_path,
                        dependencies,
                    )?;
                    println!("cargo:warning=Generated: {}", filename);
                }
            }
        }
        // Skip missing unified pages entirely - no placeholders generated
    }

    Ok(())
}

/// Direct generation without caching (fallback)
fn generate_components_direct(out_path: &Path) -> Result<(), Box<dyn std::error::Error>> {
    // Clean output directory
    clean_generated_output(&out_path)?;

    // Load route configurations
    let components = load_route_configurations()?;

    for component_info in components {
        if component_info.has_existing_unified {
            // Generate wrapper components for existing unified pages
            let content = generate_single_component_content(&component_info)?;
            let file_path = out_path.join(format!("page_{}.rs", component_info.component_name));
            fs::write(&file_path, content)?;
            println!(
                "cargo:warning=Generated page component: {}",
                component_info.component_name
            );
        }
    }

    Ok(())
}

/// Get path to route configuration file
fn get_route_config_path() -> Result<String, Box<dyn std::error::Error>> {
    // Use the same routes directory logic
    let routes_dir = get_routes_directory()?;

    // Check for any .toml file in the routes directory
    for entry in fs::read_dir(&routes_dir)? {
        let entry = entry?;
        let path = entry.path();
        if path.extension().and_then(|s| s.to_str()) == Some("toml") {
            return Ok(path.to_string_lossy().to_string());
        }
    }

    // Fallback to legacy single routes.toml file approach
    let manifest_dir = env::var("CARGO_MANIFEST_DIR")?;
    let project_root = Path::new(&manifest_dir)
        .parent()
        .and_then(|p| p.parent())
        .ok_or("Cannot determine project root")?;

    let legacy_paths = vec![
        project_root.join("site/config/routes.toml"),
        project_root.join("content/routes.toml"),
    ];

    for path in legacy_paths {
        if path.exists() {
            return Ok(path.to_string_lossy().to_string());
        }
    }

    Err("No route configuration file found".into())
}

/// Generate sophisticated wrapper component content (restored from pre_build_page_generator.rs)
fn generate_single_component_content(
    component_info: &ComponentInfo,
) -> Result<String, Box<dyn std::error::Error>> {
    // Safety check: Only generate wrappers for existing unified components
    if !component_info.has_existing_unified {
        return Err(format!(
            "Cannot generate wrapper for {}: unified component does not exist at expected path",
            component_info.component_name
        )
        .into());
    }

    let patterns_str = component_info
        .lang_prefixes
        .iter()
        .map(|p| format!("\"{}\"", p))
        .collect::<Vec<_>>()
        .join(", ");

    let _params_props = generate_params_props(&component_info.props);
    let _params_args = generate_params_args(&component_info.props);
    let _params_forward = generate_params_forward(&component_info.props);

    // For content pages, content_type should be required parameter, not optional with default
    let (content_params_props, content_params_args, content_params_forward, _content_params_clones) =
        if component_info.is_content_page {
            if let Some(toml::Value::Table(table)) = &component_info.props {
                if let Some(toml::Value::String(_content_type)) = table.get("content_type") {
                    (
                        format!("\n    content_type: String,"),
                        format!(" content_type=content_type"),
                        format!("\n            content_type=content_type.clone()"),
                        "\n    let content_type = content_type.clone();".to_string(),
                    )
                } else {
                    (
                        "".to_string(),
                        "".to_string(),
                        "".to_string(),
                        "".to_string(),
                    )
                }
            } else {
                (
                    "".to_string(),
                    "".to_string(),
                    "".to_string(),
                    "".to_string(),
                )
            }
        } else {
            (
                "".to_string(),
                "".to_string(),
                "".to_string(),
                "".to_string(),
            )
        };

    let content = format!(
        r#"
// Auto-generated wrapper components for {component_name}
// Calls existing {unified_component} from crates/pages/src/{module_path}/unified.rs

// Use qualified paths to avoid import conflicts between generated files
use leptos::prelude::*;

/// Client-side reactive page component (wrapper)
#[component]
pub fn {component_name}Client(
    #[prop(default = rustelo_core_lib::config::get_default_language().to_string())] _language: String,{content_params_props}
) -> impl IntoView {{
    let translator = rustelo_core_lib::i18n::page_translator::SsrTranslator::new(_language.clone());
    let patterns: Vec<&str> = vec![{patterns_str}];
    let lang_content = rustelo_core_lib::i18n::helpers::build_page_content_patterns(&translator, &patterns);

    view! {{
        <{unified_component}
            _language=_language.clone()
            lang_content=lang_content{content_params_forward}
        />
    }}
}}

/// SSR static page component (wrapper)
#[component]
pub fn {component_name}SSR(
    #[prop(default = rustelo_core_lib::config::get_default_language().to_string())] _language: String,{content_params_props}
) -> impl IntoView {{
    // Create static translator for SSR
    let translator = rustelo_core_lib::i18n::page_translator::SsrTranslator::new(_language.clone());
    let patterns: Vec<&str> = vec![{patterns_str}];
    let lang_content = rustelo_core_lib::i18n::helpers::build_page_content_patterns(&translator, &patterns);

    view! {{
        <{unified_component}
            _language=_language.clone()
            lang_content=lang_content{content_params_forward}
        />
    }}
}}

/// Main page component with delegation
#[component]
pub fn {component_name}Page(
    #[prop(default = rustelo_core_lib::config::get_default_language().to_string())] _language: String,{content_params_props}
) -> impl IntoView {{
    #[cfg(not(target_arch = "wasm32"))]
    {{
        view! {{ <{component_name}SSR _language=_language.clone(){content_params_args} /> }}
    }}

    #[cfg(target_arch = "wasm32")]
    {{
        view! {{ <{component_name}Client _language=_language.clone(){content_params_args} /> }}
    }}
}}
"#,
        component_name = component_info.component_name,
        unified_component = component_info.unified_component,
        module_path = component_info._module_path,
        patterns_str = patterns_str,
        content_params_props = content_params_props,
        content_params_args = content_params_args,
        content_params_forward = content_params_forward,
    );

    Ok(content)
}

/// Get the routes directory using SITE_CONFIG_PATH environment variable or fallback paths
fn get_routes_directory() -> Result<std::path::PathBuf, Box<dyn std::error::Error>> {
    // First, try to use SITE_CONFIG_PATH environment variable
    if let Ok(config_path) = env::var("SITE_CONFIG_PATH") {
        let manifest_dir = env::var("CARGO_MANIFEST_DIR")?;
        let project_root = Path::new(&manifest_dir).parent().unwrap().parent().unwrap(); // Go up from crates/pages to project root

        // Try both absolute and relative paths for config_path
        let potential_routes_dirs = vec![
            Path::new(&config_path).join("routes"),  // Absolute path
            project_root.join(&config_path).join("routes"),  // Relative to project root
        ];

        for routes_dir in potential_routes_dirs {
            if routes_dir.exists() {
                return Ok(routes_dir);
            }
        }
    }

    // Fallback to project root-relative paths
    let manifest_dir = env::var("CARGO_MANIFEST_DIR")?;
    let project_root = Path::new(&manifest_dir).parent().unwrap().parent().unwrap(); // Go up from crates/pages to project root

    let fallback_paths = vec![
        project_root.join("site/config/routes"),
        project_root.join("content/config/routes"),
        project_root.join("config/routes"),
    ];

    for routes_dir in fallback_paths {
        if routes_dir.exists() {
            println!("cargo:warning=Using fallback routes directory: {}", routes_dir.display());
            return Ok(routes_dir);
        }
    }

    Err("No routes directory found. Please set SITE_CONFIG_PATH or create routes at site/config/routes/".into())
}

/// Load route configurations with deduplication by component_name
fn load_route_configurations() -> Result<Vec<ComponentInfo>, Box<dyn std::error::Error>> {
    use std::collections::HashMap;

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

    // Use SITE_CONFIG_PATH environment variable or fallback to common paths
    let routes_dir = get_routes_directory()?;

    if !routes_dir.exists() {
        return Err(format!(
            "Routes directory not found: {}. Please ensure route configuration files exist.",
            routes_dir.display()
        )
        .into());
    }

    // Read all .toml files
    for entry in fs::read_dir(&routes_dir)? {
        let entry = entry?;
        let path = entry.path();

        if path.extension().and_then(|s| s.to_str()) == Some("toml") {
            let content = fs::read_to_string(&path)?;
            let routes_config: RoutesConfig = toml::from_str(&content)?;

            for route in routes_config.routes {
                if route.enabled {
                    let component_info = process_route_config(route)?;

                    // Only add if component doesn't already exist - deduplication by component name
                    if !components_map.contains_key(&component_info.component_name) {
                        components_map
                            .insert(component_info.component_name.clone(), component_info);
                    }
                }
            }
        }
    }

    if components_map.is_empty() {
        return Err("No enabled routes found in configuration files. Please check your route configuration.".into());
    }

    let components: Vec<ComponentInfo> = components_map.into_values().collect();
    Ok(components)
}

/// Process route config into ComponentInfo
fn process_route_config(route: RouteConfig) -> Result<ComponentInfo, Box<dyn std::error::Error>> {
    // Extract component name from page_component (remove "Page" suffix if present)
    let component_name = if route.page_component.ends_with("Page") {
        route.page_component.trim_end_matches("Page").to_string()
    } else {
        route.page_component.clone()
    };
    let unified_component = route
        .unified_component
        .unwrap_or_else(|| format!("Unified{}Page", component_name));

    let lang_prefixes = route
        .lang_prefixes
        .unwrap_or_else(|| vec![format!("{}-", component_name.to_lowercase())]);

    let props = route.props.clone();
    let is_content_page = if let Some(toml::Value::Table(ref table)) = props {
        table.contains_key("content_type")
    } else {
        false
    };

    // Check if unified component exists using module_path from route config
    let manifest_dir = env::var("CARGO_MANIFEST_DIR")?;

    let component_path = if let Some(module_path) = &route.module_path {
        module_path.clone()
    } else {
        // Fallback to component name in lowercase if no module_path specified
        component_name.to_lowercase()
    };

    let component_dir = Path::new(&manifest_dir).join("src").join(&component_path);
    let unified_path = component_dir.join("unified.rs");
    let mod_path = component_dir.join("mod.rs");

    // Check if unified.rs exists OR if there's a mod.rs that might export the component
    // Also check if the unified component is already defined in lib.rs
    let has_existing_unified = unified_path.exists() || mod_path.exists();

    // Debug path checking removed for cleaner output

    // Check if the *Page component (not the Unified*Page) already exists
    // If the Page component exists, we should skip generation
    let page_component_name = format!("{}Page", component_name);
    let skip_generation = if let Some(module_path) = &route.module_path {
        // Check if there's already a Page component in the module
        let module_dir = Path::new(&manifest_dir).join("src").join(module_path);
        let page_exists_in_module = [module_dir.join("mod.rs"), module_dir.join("unified.rs")]
            .iter()
            .any(|path| {
                if path.exists() {
                    let content = fs::read_to_string(path).unwrap_or_default();
                    // Look for the actual Page component (like ContentCategoryPage) being defined
                    content.contains(&format!("pub fn {}(", page_component_name))
                } else {
                    false
                }
            });

        // ContentCategory skip debug removed for cleaner output

        page_exists_in_module
    } else {
        false
    };

    // If skip_generation is true, it means the component already exists and is properly exported
    // so we should NOT generate a wrapper (set has_existing_unified = false to skip generation)
    let final_has_existing_unified = if skip_generation {
        false // Don't generate wrapper - component already exists and is exported
    } else {
        has_existing_unified // Generate wrapper if unified component exists but not exported
    };

    // ContentCategory final debug removed for cleaner output

    Ok(ComponentInfo {
        component_name,
        unified_component,
        lang_prefixes,
        props,
        has_existing_unified: final_has_existing_unified,
        is_content_page,
        _module_path: component_path,
    })
}

// generate_basic_component function removed - no placeholders generated

/// Clean the generated output directory
fn clean_generated_output(out_dir: &Path) -> Result<(), Box<dyn std::error::Error>> {
    if out_dir.exists() {
        // Remove all page_*.rs files from previous builds
        for entry in fs::read_dir(out_dir)? {
            let entry = entry?;
            let path = entry.path();
            if let Some(file_name) = path.file_name().and_then(|n| n.to_str()) {
                if file_name.starts_with("page_") && file_name.ends_with(".rs") {
                    fs::remove_file(&path)?;
                }
            }
        }
    }
    Ok(())
}

// No fallback components - route configuration is mandatory
// This ensures the system stays fully configuration-driven

// Helper functions for parameter generation
fn generate_params_props(params: &Option<toml::Value>) -> String {
    if let Some(toml::Value::Table(table)) = params {
        if table.is_empty() {
            return String::new();
        }

        let props: Vec<String> = table
            .iter()
            .map(|(key, value)| {
                let prop_type = match value {
                    toml::Value::String(_) => "Option<String>",
                    toml::Value::Boolean(_) => "Option<bool>",
                    toml::Value::Integer(_) => "Option<i64>",
                    toml::Value::Float(_) => "Option<f64>",
                    toml::Value::Array(_) => "Option<Vec<String>>",
                    _ => "Option<String>", // fallback
                };
                format!("\n    #[prop(optional)] {}: {},", key, prop_type)
            })
            .collect();

        props.join("")
    } else {
        String::new()
    }
}

fn generate_params_args(params: &Option<toml::Value>) -> String {
    if let Some(toml::Value::Table(table)) = params {
        if table.is_empty() {
            return String::new();
        }

        let args: Vec<String> = table
            .keys()
            .map(|key| format!(" {}={}", key, key))
            .collect();

        args.join("")
    } else {
        String::new()
    }
}

fn generate_params_forward(params: &Option<toml::Value>) -> String {
    if let Some(toml::Value::Table(table)) = params {
        if table.is_empty() {
            return String::new();
        }

        let forwards: Vec<String> = table
            .keys()
            .map(|key| format!("\n            {}={}", key, key))
            .collect();

        forwards.join("")
    } else {
        String::new()
    }
}

// Unused functions removed to eliminate warnings
