//! Build script for Fresh editor
//!
//! Generates TypeScript type definitions from Rust op definitions.
//! JSON Schema for configuration is now generated via `cargo run --features dev-bins --bin generate_schema`.

use std::fs;
use std::path::Path;

fn main() {
    // Rerun if locales change
    println!("cargo::rerun-if-changed=locales");

    // Always generate locale_options.rs - it's required by config.rs at compile time
    // This must run even during publish verification since the include!() macro needs it
    if let Err(e) = generate_locale_options() {
        eprintln!("Warning: Failed to generate locale options: {}", e);
    }

    // Generate plugins content hash for cache invalidation
    #[cfg(feature = "embed-plugins")]
    {
        println!("cargo::rerun-if-changed=plugins");
        if let Err(e) = generate_plugins_hash() {
            eprintln!("Warning: Failed to generate plugins hash: {}", e);
        }
    }
}

/// Generate a hash of all plugin files for cache invalidation
#[cfg(feature = "embed-plugins")]
fn generate_plugins_hash() -> Result<(), Box<dyn std::error::Error>> {
    use std::collections::hash_map::DefaultHasher;
    use std::hash::Hasher;

    let plugins_dir = Path::new("plugins");
    let mut hasher = DefaultHasher::new();

    // Hash all files in the plugins directory recursively
    hash_directory(plugins_dir, &mut hasher)?;

    let hash = format!("{:016x}", hasher.finish());

    let out_dir = std::env::var("OUT_DIR")?;
    let dest_path = Path::new(&out_dir).join("plugins_hash.txt");
    fs::write(&dest_path, &hash)?;

    println!("cargo::warning=Generated plugins hash: {}", hash);
    Ok(())
}

#[cfg(feature = "embed-plugins")]
fn hash_directory(dir: &Path, hasher: &mut impl std::hash::Hasher) -> std::io::Result<()> {
    use std::hash::Hash;

    if !dir.exists() {
        return Ok(());
    }

    let mut entries: Vec<_> = fs::read_dir(dir)?.filter_map(|e| e.ok()).collect();
    // Sort for deterministic ordering
    entries.sort_by_key(|e| e.path());

    for entry in entries {
        let path = entry.path();
        // Hash the relative path
        path.strip_prefix("plugins").unwrap_or(&path).hash(hasher);

        if path.is_dir() {
            hash_directory(&path, hasher)?;
        } else {
            // Hash file contents
            let contents = fs::read(&path)?;
            contents.hash(hasher);
        }
    }

    Ok(())
}

/// Generate a Rust file with the list of available locales from the locales directory
fn generate_locale_options() -> Result<(), Box<dyn std::error::Error>> {
    let locales_dir = Path::new("locales");

    // Read all .json files in the locales directory
    let mut locales: Vec<String> = fs::read_dir(locales_dir)?
        .filter_map(|entry| {
            let entry = entry.ok()?;
            let path = entry.path();
            if path.extension()? == "json" {
                path.file_stem()?.to_str().map(|s| s.to_string())
            } else {
                None
            }
        })
        .collect();

    // Sort alphabetically for consistent output
    locales.sort();

    // Generate Rust code
    let out_dir = std::env::var("OUT_DIR")?;
    let dest_path = Path::new(&out_dir).join("locale_options.rs");

    let locale_entries: Vec<String> = locales.iter().map(|l| format!("Some(\"{}\")", l)).collect();

    let content = format!(
        r#"// Auto-generated by build.rs from locales/*.json files
// DO NOT EDIT MANUALLY

/// Available locale options for the settings dropdown
/// None (null) means auto-detect from environment
pub const GENERATED_LOCALE_OPTIONS: &[Option<&str>] = &[
    None, // Auto-detect
    {}
];
"#,
        locale_entries.join(",\n    ")
    );

    // Note: OUT_DIR files don't need write_if_changed since cargo handles them specially,
    // but it doesn't hurt to use it for consistency
    fs::write(&dest_path, content)?;

    println!(
        "cargo::warning=Generated locale options with {} locales",
        locales.len()
    );

    Ok(())
}
