//! High-level API for the codegen crate
//!
//! This module provides the main entry points that build.rs files will call

use std::env;
use std::path::Path;
use std::fs;
use crate::hash_content;

/// Generate all shared resources (routes, content types, resources, etc.)
/// Called from crates/shared/build.rs
pub fn generate_shared_resources() -> Result<(), Box<dyn std::error::Error>> {
    // Setup cargo configuration flags
    crate::build::setup_cargo_config();

    // Get environment variables with error handling
    let out_dir = match env::var("OUT_DIR") {
        Ok(dir) => dir,
        Err(e) => {
            eprintln!("Warning: Could not get OUT_DIR: {e}. Using fallback resources.");
            crate::build::generate_fallback_registry();
            return Ok(());
        }
    };

    let manifest_dir = match env::var("CARGO_MANIFEST_DIR") {
        Ok(dir) => dir,
        Err(e) => {
            eprintln!("Warning: Could not get CARGO_MANIFEST_DIR: {e}. Using fallback resources.");
            crate::build::generate_fallback_registry();
            return Ok(());
        }
    };

    // Get root path from environment or detect project root
    let root_path = match crate::build::get_root_path(&manifest_dir) {
        Ok(path) => path,
        Err(e) => {
            eprintln!("Warning: Could not determine root path: {e}. Using fallback resources.");
            crate::build::generate_fallback_registry();
            return Ok(());
        }
    };

    // Get content root path (configurable via SITE_CONTENT_PATH)
    let content_root = crate::build::get_content_root_path(&root_path);
    println!("cargo:warning=Using content root path: {}", content_root);

    // Setup cargo rerun triggers
    crate::build::setup_cargo_rerun_triggers(&root_path, &content_root);

    // Execute shared build tasks in order
    let build_success = execute_shared_build_tasks(&root_path, &content_root, &out_dir);

    // Only set cfg flag if ALL critical tasks succeeded
    if build_success {
        println!("cargo:rustc-cfg=has_generated_resources");
        println!("cargo:warning=All build tasks completed successfully - using generated resources");
    } else {
        println!("cargo:warning=Some build tasks failed - using fallback resources");
    }

    // Copy generated files for development inspection if enabled
    copy_generated_files_for_dev(&manifest_dir, &out_dir);

    Ok(())
}

/// Run page scaffolding with smart cache integration
/// Called from crates/pages/build.rs
pub fn run_pages_scaffolding() -> Result<(), Box<dyn std::error::Error>> {
    println!("cargo:rerun-if-changed=../../site/config/routes/");
    println!("cargo:rerun-if-env-changed=ENVIRONMENT");
    println!("cargo:rerun-if-env-changed=PROFILE");

    // Get project root path
    let manifest_dir = env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set");
    let project_root = Path::new(&manifest_dir)
        .parent()
        .and_then(|p| p.parent())
        .expect("Cannot determine project root");

    // Try to run with smart cache first, fallback to direct scaffolding
    match run_page_scaffolding_with_cache(project_root) {
        Ok(changes) => {
            if changes > 0 {
                println!(
                    "cargo:warning=Page scaffolding completed successfully - {} changes made",
                    changes
                );
            }
        }
        Err(e) => {
            println!("cargo:warning=Smart cache page scaffolding failed: {}, falling back to direct scaffolding", e);
            // Fallback to direct scaffolding without cache
            match crate::build::run_page_scaffolding(project_root) {
                Ok(changes) => {
                    if changes > 0 {
                        println!(
                            "cargo:warning=Direct page scaffolding completed successfully - {} changes made",
                            changes
                        );
                    }
                }
                Err(fallback_e) => {
                    println!("cargo:warning=Page scaffolding failed: {}", fallback_e);
                }
            }
        }
    }

    Ok(())
}

/// Execute all build tasks for shared crate with proper error handling
/// Returns true if all critical tasks succeeded, false if any failed
fn execute_shared_build_tasks(_root_path: &str, content_root: &str, out_dir: &str) -> bool {
    let mut all_tasks_successful = true;

    // Task 0: Generate configuration constants (CRITICAL for WASM compatibility)
    if let Err(e) = crate::build::config_constants::generate_config_constants(out_dir) {
        eprintln!("Warning: Failed to generate config constants: {e}");
        all_tasks_successful = false;
    } else {
        println!("cargo:warning=Successfully generated configuration constants");
    }

    // Task 1: Embed routes.toml content for both SSR and client builds (CRITICAL)
    if let Err(e) = crate::build::embed_routes_config(content_root, out_dir) {
        eprintln!("Warning: Failed to embed routes config: {e}");
        all_tasks_successful = false;
    }

    // Task 2: Generate route component definitions from SITE_CONFIG_PATH/routes/
    let site_config_path =
        std::env::var("SITE_CONFIG_PATH").unwrap_or_else(|_| "site/config".to_string());
    println!(
        "cargo:warning=Attempting to generate route components from {}/routes/",
        site_config_path
    );
    match crate::build::generate_route_components(&content_root, out_dir) {
        Ok(()) => println!("cargo:warning=Successfully generated route components"),
        Err(e) => {
            eprintln!("Warning: Failed to generate route components: {e}");
            println!("cargo:warning=Route components generation failed: {e}");
            all_tasks_successful = false;
        }
    }

    // Task 2.5: Generate page implementations from templates for auto_generate routes
    println!("cargo:warning=Attempting to generate pages from templates");
    match crate::build::route_generation::load_routes_config(content_root) {
        Ok(routes_config) => {
            match crate::build::generate_pages_from_templates(&routes_config, out_dir) {
                Ok(()) => println!("cargo:warning=Successfully processed page template generation"),
                Err(e) => {
                    eprintln!("Warning: Failed to generate pages from templates: {e}");
                    println!("cargo:warning=Page template generation failed: {e}");
                }
            }
        }
        Err(e) => {
            eprintln!("Warning: Failed to load routes config for page generation: {e}");
        }
    }

    // Task 3: Generate page boilerplate for generate_boilerplate_only routes
    match crate::build::route_generation::load_routes_config(content_root) {
        Ok(routes_config) => {
            match crate::build::generate_page_boilerplate(&routes_config, out_dir) {
                Ok(()) => {
                    println!("cargo:warning=Successfully processed page boilerplate generation")
                }
                Err(e) => {
                    eprintln!("Warning: Failed to generate page boilerplate: {e}");
                    println!("cargo:warning=Page boilerplate generation failed: {e}");
                }
            }
        }
        Err(e) => {
            eprintln!("Warning: Failed to load routes config for boilerplate generation: {e}");
        }
    }

    // Task 4: Generate content type enums with operational knowledge (CRITICAL)
    match crate::build::generate_content_types(content_root, out_dir) {
        Ok(()) => println!("cargo:warning=Successfully generated content types"),
        Err(e) => {
            eprintln!("Warning: Failed to generate content types: {e}");
            println!("cargo:warning=Content types generation failed: {e}");
            all_tasks_successful = false;
        }
    }

    // Task 5: Generate resource registry with all discovered resources
    println!(
        "cargo:warning=Attempting to generate resource registry from {}/",
        content_root
    );
    match crate::build::generate_resource_registry(content_root, out_dir) {
        Ok(()) => println!("cargo:warning=Successfully generated resource registry"),
        Err(e) => {
            eprintln!("Warning: Failed to generate resource registry: {e}");
            println!("cargo:warning=Resource registry generation failed: {e}");
        }
    }

    // Task 6: Generate comprehensive route documentation
    let site_codegen_path =
        std::env::var("SITE_INFO_PATH").unwrap_or_else(|_| "site/codegen".to_string());
    let codegen_path = format!("{}/{}", _root_path, site_codegen_path);

    println!(
        "cargo:warning=Generating route documentation to {}",
        codegen_path
    );

    // Generate comprehensive route documentation with new structure
    match crate::build::generate_comprehensive_documentation(
        content_root,
        &codegen_path,
        _root_path,
    ) {
        Ok(()) => {
            println!("cargo:warning=Successfully generated comprehensive route documentation")
        }
        Err(e) => {
            eprintln!("Warning: Failed to generate comprehensive route documentation: {e}");
            println!("cargo:warning=Comprehensive route documentation generation failed: {e}");
        }
    }

    all_tasks_successful
}

/// Run page scaffolding with smart cache integration
fn run_page_scaffolding_with_cache(project_root: &Path) -> Result<usize, Box<dyn std::error::Error>> {
    use crate::smart_cache::{SmartCache, CacheStatus, hash_content, is_cache_available};
    use std::fs;

    // Only proceed with cache if available
    if !is_cache_available() {
        println!("cargo:warning=Smart cache not available - using direct page scaffolding");
        return crate::build::run_page_scaffolding(project_root);
    }

    // Initialize cache for page scaffolding
    let cache = SmartCache::new("page-scaffolding")?;

    // Calculate dependencies for page scaffolding (route configs + existing pages structure)
    let dependencies = get_scaffolding_dependencies(project_root)?;
    let combined_content = dependencies.iter()
        .filter_map(|path| fs::read_to_string(path).ok())
        .collect::<Vec<_>>()
        .join("\n");
    let source_hash = hash_content(&combined_content);

    // Check if scaffolding state has changed
    let scaffolding_state_file = "scaffolding_state.json";
    match cache.check_file_cache(scaffolding_state_file, &source_hash)? {
        CacheStatus::Fresh(_) => {
            // No changes needed - routes and pages are unchanged
            println!("cargo:warning=Page scaffolding cached and up to date");
            return Ok(0);
        }
        CacheStatus::Stale(_) | CacheStatus::Missing(_) => {
            // Need to run scaffolding - something has changed
            println!("cargo:warning=Running page scaffolding - routes or pages have changed");
            let changes = crate::build::run_page_scaffolding(project_root)?;

            // Update cache with new state
            let state_content = serde_json::json!({
                "timestamp": chrono::Utc::now().to_rfc3339(),
                "dependencies": dependencies,
                "changes": changes
            }).to_string();

            cache.update_cache(
                scaffolding_state_file,
                &state_content,
                &source_hash,
                &std::path::PathBuf::from("/tmp"), // Dummy out_dir, not used for state files
                dependencies,
            )?;

            return Ok(changes);
        }
    }
}

/// Get dependencies that affect page scaffolding
fn get_scaffolding_dependencies(project_root: &Path) -> Result<Vec<String>, Box<dyn std::error::Error>> {
    let mut dependencies = Vec::new();

    // Route configuration files
    let routes_dir = project_root.join("site/config/routes");
    if routes_dir.exists() {
        for entry in fs::read_dir(&routes_dir)? {
            let entry = entry?;
            if entry.path().extension().and_then(|s| s.to_str()) == Some("toml") {
                dependencies.push(entry.path().to_string_lossy().to_string());
            }
        }
    }

    // Pages source structure (to detect if pages were manually created)
    let pages_src_dir = project_root.join("crates/pages/src");
    if pages_src_dir.exists() {
        // Add structure hash of pages directory (not content, just what exists)
        dependencies.push(format!("pages_structure:{}", get_directory_structure_hash(&pages_src_dir)?));
    }

    Ok(dependencies)
}

/// Get a hash representing the directory structure (not content)
fn get_directory_structure_hash(dir: &Path) -> Result<String, Box<dyn std::error::Error>> {
    use std::collections::BTreeSet;

    let mut dirs = BTreeSet::new();

    fn collect_dirs(dir: &Path, base: &Path, set: &mut BTreeSet<String>) -> std::io::Result<()> {
        for entry in fs::read_dir(dir)? {
            let entry = entry?;
            if entry.file_type()?.is_dir() {
                let relative_path = entry.path().strip_prefix(base)
                    .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?
                    .to_string_lossy().to_string();
                set.insert(relative_path);
                collect_dirs(&entry.path(), base, set)?;
            }
        }
        Ok(())
    }

    collect_dirs(dir, dir, &mut dirs)?;

    let combined = dirs.into_iter().collect::<Vec<_>>().join("|");
    Ok(hash_content(&combined))
}

/// Copy generated files for development inspection if enabled
fn copy_generated_files_for_dev(manifest_dir: &str, out_dir: &str) {
    if env::var("INSPECT_GENERATED").is_ok() {
        let dest_dir = Path::new(manifest_dir).join("generated");
        if let Err(e) = std::fs::create_dir_all(&dest_dir) {
            eprintln!("Warning: Failed to create generated directory: {e}");
            return;
        }

        // Copy relevant generated files
        let files_to_copy = [
            "generated_routes.rs",
            "embedded_routes.toml",
            "resource_registry.rs",
            "content_kinds_generated.rs",
        ];

        for file in &files_to_copy {
            let src = Path::new(out_dir).join(file);
            let dst = dest_dir.join(file);
            if src.exists() {
                if let Err(e) = std::fs::copy(&src, &dst) {
                    eprintln!("Warning: Failed to copy {}: {e}", file);
                } else {
                    println!("cargo:warning=Copied generated file to {}", dst.display());
                }
            }
        }
    }
}
