//! Boilerplate generation for page components

use super::types::{Route, RoutesConfig};
use super::utils::{format_patterns_for_rust_code, get_module_name_from_page_component_with_route};
use crate::utils::RouteMetadataAccess;

/// Generate client and SSR boilerplate for existing pages while preserving
/// unified components
pub fn generate_page_boilerplate(
    routes_config: &RoutesConfig,
    out_dir: &str,
) -> Result<(), String> {
    let mut generated_count = 0;

    for route in &routes_config.routes {
        if route.get_enabled()
            && route
                .get_metadata("generate_boilerplate_only")
                .map(|s| s.parse().unwrap_or(false))
                .unwrap_or(false)
        {
            // Get i18n patterns from route configuration
            let i18n_patterns = route
                .get_metadata("i18n_patterns")
                .map(|s| s.split(',').map(|p| p.trim().to_string()).collect())
                .unwrap_or_else(|| {
                    // Default pattern based on component name
                    let component_lower = route.component.to_lowercase();
                    vec![format!("{}-", component_lower)]
                });

            // Get the page component name
            let default_page_component = format!("{}Page", route.component);
            let page_component_metadata = route.get_metadata("page_component");
            let page_component = page_component_metadata
                .as_deref()
                .unwrap_or(&default_page_component);

            // Get the unified component name
            let default_unified_component = format!("Unified{}Page", route.component);
            let unified_component_metadata = route.get_metadata("unified_component");
            let unified_component = unified_component_metadata
                .as_deref()
                .unwrap_or(&default_unified_component);

            let module_name = get_module_name_from_page_component_with_route(route);

            // Check if the page directory exists
            let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap_or_default();
            let project_root = std::path::Path::new(&manifest_dir)
                .parent()
                .unwrap()
                .parent()
                .unwrap();
            let page_dir = project_root.join("crates/pages/src").join(&module_name);
            if !page_dir.exists() {
                println!(
                    "cargo:warning=Skipping boilerplate generation for {} - page directory {} \
                     does not exist",
                    page_component,
                    page_dir.display()
                );
                continue;
            }

            // Generate client.rs boilerplate
            let client_content = generate_client_boilerplate(
                &route.component,
                unified_component,
                &i18n_patterns,
                route,
            )?;

            // Generate ssr.rs boilerplate
            let ssr_content = generate_ssr_boilerplate(
                &route.component,
                unified_component,
                &i18n_patterns,
                route,
            )?;

            // Write files to the generated directory structure
            let page_out_dir = std::path::Path::new(out_dir)
                .join("pages")
                .join("boilerplate")
                .join(&module_name);

            std::fs::create_dir_all(&page_out_dir).map_err(|e| {
                format!(
                    "Failed to create output directory {}: {}",
                    page_out_dir.display(),
                    e
                )
            })?;

            std::fs::write(page_out_dir.join("client.rs"), &client_content)
                .map_err(|e| format!("Failed to write client.rs: {}", e))?;

            std::fs::write(page_out_dir.join("ssr.rs"), &ssr_content)
                .map_err(|e| format!("Failed to write ssr.rs: {}", e))?;

            // Handle replacement if enabled
            if route
                .get_metadata("replace_existing_boilerplate")
                .map(|s| s.parse().unwrap_or(false))
                .unwrap_or(false)
            {
                handle_file_replacement(&page_dir, &client_content, &ssr_content)?;
            }

            generated_count += 1;
            println!(
                "cargo:warning=Generated boilerplate for {} (client.rs + ssr.rs)",
                page_component
            );
        }
    }

    if generated_count > 0 {
        println!(
            "cargo:warning=Successfully generated boilerplate for {} pages",
            generated_count
        );
    }

    Ok(())
}

/// Generate client.rs boilerplate content
fn generate_client_boilerplate(
    component_name: &str,
    unified_component: &str,
    i18n_patterns: &[String],
    route: &Route,
) -> Result<String, String> {
    let module_name = get_module_name_from_page_component_with_route(route);
    let patterns_str = format_patterns_for_rust_code(i18n_patterns);

    let mut content = format!(
        r#"//! Generated client-side {} page component
//!
//! Auto-generated reactive implementation with i18n pattern discovery.

use crate::{}::unified::{};
use leptos::prelude::*;
use rustelo_core_types::{{
    config::get_default_language,
    i18n::{{build_page_content_patterns, UnifiedI18n}},
    state::use_language,
}};

/// Client-side reactive {} page component
#[component]
pub fn {}Client(
    #[allow(unused_variables)]
    #[prop(default = get_default_language().to_string())]
    language: String,"#,
        component_name, module_name, unified_component, component_name, component_name
    );

    // Add additional parameters based on route configuration
    if route
        .get_metadata("content_type_param")
        .map(|s| s.parse().unwrap_or(false))
        .unwrap_or(false)
    {
        content.push_str("\n    #[prop(optional)] content_type: Option<String>,");
    }
    if route
        .get_metadata("category_param")
        .map(|s| s.parse().unwrap_or(false))
        .unwrap_or(false)
    {
        content.push_str("\n    #[prop(optional)] category: Option<String>,");
    }
    if route
        .get_metadata("slug_param")
        .map(|s| s.parse().unwrap_or(false))
        .unwrap_or(false)
    {
        content.push_str("\n    #[prop(optional)] slug: Option<String>,");
    }

    content.push_str(&format!(
        r#"
) -> impl IntoView {{
    // Get language context outside the memo to ensure we track the same instance
    let language_context = use_language();

    // Create page content with reactive language tracking
    let page_content = Memo::new(move |_| {{
        let current_language = language_context.current.get();
        let i18n = UnifiedI18n::new(&current_language, "/");
        build_page_content_patterns(&i18n, {})
    }});

    move || {{
        view! {{
            <{} lang_content=page_content.get()"#,
        patterns_str, unified_component
    ));

    // Add props to the view call
    if route
        .get_metadata("content_type_param")
        .map(|s| s.parse().unwrap_or(false))
        .unwrap_or(false)
    {
        content.push_str(" content_type=content_type.clone()");
    }
    if route
        .get_metadata("category_param")
        .map(|s| s.parse().unwrap_or(false))
        .unwrap_or(false)
    {
        content.push_str(" category=category.clone()");
    }
    if route
        .get_metadata("slug_param")
        .map(|s| s.parse().unwrap_or(false))
        .unwrap_or(false)
    {
        content.push_str(" slug=slug.clone()");
    }

    content.push_str(" />\n        }\n    }\n}\n");

    Ok(content)
}

/// Generate ssr.rs boilerplate content
fn generate_ssr_boilerplate(
    component_name: &str,
    unified_component: &str,
    i18n_patterns: &[String],
    route: &Route,
) -> Result<String, String> {
    let module_name = get_module_name_from_page_component_with_route(route);
    let patterns_str = format_patterns_for_rust_code(i18n_patterns);

    let mut content = format!(
        r#"//! Generated SSR {} page component
//!
//! Auto-generated server-side rendered page with pattern-based key discovery.

use leptos::prelude::*;
use rustelo_core_types::{{
    i18n::{{build_page_content_patterns, SsrTranslator}},
    config::get_default_language,
}};

/// SSR {} Page with pattern-based key discovery
#[component]
pub fn {}SSR(
    #[prop(default = get_default_language().to_string())] language: String,"#,
        component_name, component_name, component_name
    );

    // Add additional parameters based on route configuration
    if route
        .get_metadata("content_type_param")
        .map(|s| s.parse().unwrap_or(false))
        .unwrap_or(false)
    {
        content.push_str("\n    #[prop(optional)] content_type: Option<String>,");
    }
    if route
        .get_metadata("category_param")
        .map(|s| s.parse().unwrap_or(false))
        .unwrap_or(false)
    {
        content.push_str("\n    #[prop(optional)] category: Option<String>,");
    }
    if route
        .get_metadata("slug_param")
        .map(|s| s.parse().unwrap_or(false))
        .unwrap_or(false)
    {
        content.push_str("\n    #[prop(optional)] slug: Option<String>,");
    }

    content.push_str(&format!(
        r#"
) -> impl IntoView {{
    let ssr_i18n = SsrTranslator::new(language);
    let content = build_page_content_patterns(&ssr_i18n, {});

    view! {{
        <crate::{}::unified::{} lang_content=content"#,
        patterns_str, module_name, unified_component
    ));

    // Add props to the view call
    if route
        .get_metadata("content_type_param")
        .map(|s| s.parse().unwrap_or(false))
        .unwrap_or(false)
    {
        content.push_str(" content_type=content_type");
    }
    if route
        .get_metadata("category_param")
        .map(|s| s.parse().unwrap_or(false))
        .unwrap_or(false)
    {
        content.push_str(" category=category");
    }
    if route
        .get_metadata("slug_param")
        .map(|s| s.parse().unwrap_or(false))
        .unwrap_or(false)
    {
        content.push_str(" slug=slug");
    }

    content.push_str(" />\n    }\n}\n");

    Ok(content)
}

/// Handle file replacement if enabled
fn handle_file_replacement(
    page_dir: &std::path::Path,
    client_content: &str,
    ssr_content: &str,
) -> Result<(), String> {
    let existing_client_path = page_dir.join("client.rs");
    let existing_ssr_path = page_dir.join("ssr.rs");

    // Backup and replace client.rs
    if existing_client_path.exists() {
        let backup_path = page_dir.join("client.rs.backup");
        if let Err(e) = std::fs::copy(&existing_client_path, &backup_path) {
            println!(
                "cargo:warning=Failed to backup {}: {}",
                existing_client_path.display(),
                e
            );
        } else {
            println!(
                "cargo:warning=Backed up {} to {}",
                existing_client_path.display(),
                backup_path.display()
            );
        }

        if let Err(e) = std::fs::write(&existing_client_path, client_content) {
            println!(
                "cargo:warning=Failed to replace {}: {}",
                existing_client_path.display(),
                e
            );
        } else {
            println!(
                "cargo:warning=Replaced {} with generated boilerplate",
                existing_client_path.display()
            );
        }
    }

    // Backup and replace ssr.rs
    if existing_ssr_path.exists() {
        let backup_path = page_dir.join("ssr.rs.backup");
        if let Err(e) = std::fs::copy(&existing_ssr_path, &backup_path) {
            println!(
                "cargo:warning=Failed to backup {}: {}",
                existing_ssr_path.display(),
                e
            );
        } else {
            println!(
                "cargo:warning=Backed up {} to {}",
                existing_ssr_path.display(),
                backup_path.display()
            );
        }

        if let Err(e) = std::fs::write(&existing_ssr_path, ssr_content) {
            println!(
                "cargo:warning=Failed to replace {}: {}",
                existing_ssr_path.display(),
                e
            );
        } else {
            println!(
                "cargo:warning=Replaced {} with generated boilerplate",
                existing_ssr_path.display()
            );
        }
    }

    Ok(())
}
