//! FTL (Fluent) content loader with language-agnostic functionality
//! Simplified to contain only essential functions for loading translations

use std::collections::HashMap;
use std::error::Error;
use std::path::{Path, PathBuf};

use fluent_bundle::{FluentBundle, FluentResource};
use fluent_syntax::ast;
use toml::de;
use tracing::debug;

/// Discover available languages from embedded resources (WASM-compatible)
fn discover_languages_from_embedded_resources() -> Vec<String> {
    use crate::resources::load;

    let debug_value = crate::get_debug_value();
    let available_ftl = load::available_ftl_resources();
    let mut languages = std::collections::HashSet::new();

    // Extract language codes from resource keys like "en_auth", "es_common", etc.
    for resource_key in available_ftl {
        if let Some(underscore_pos) = resource_key.find('_') {
            let lang_code = &resource_key[..underscore_pos];
            if lang_code.len() >= 2
                && lang_code.len() <= 5
                && lang_code.chars().all(|c| c.is_alphanumeric() || c == '-')
            {
                languages.insert(lang_code.to_string());
            }
        }
    }

    let mut result: Vec<String> = languages.into_iter().collect();
    result.sort();

    if result.is_empty() {
        if debug_value > 0 {
            tracing::debug!("No languages discovered from embedded resources, defaulting to 'en'");
        }
        result.push("en".to_string());
    }

    if debug_value > 3 {
        tracing::debug!(
            "Discovered available languages from embedded resources: {:?}",
            result
        );
    }
    result
}

/// Discover available languages from the content/locales directory
fn discover_available_languages(root_path: &Path) -> Vec<String> {
    let site_i18n_path =
        std::env::var("SITE_I18N_PATH").unwrap_or_else(|_| "site/i18n".to_string());
    let locales_dir = root_path.join(&site_i18n_path).join("locales");
    let debug_value = crate::get_debug_value();
    let mut languages = Vec::new();

    if let Ok(entries) = std::fs::read_dir(&locales_dir) {
        for entry in entries.filter_map(|e| e.ok()) {
            let path = entry.path();
            if path.is_dir() {
                if let Some(dir_name) = path.file_name().and_then(|n| n.to_str()) {
                    // Validate that this looks like a language code (2-5 characters, alphanumeric
                    // or dash)
                    if dir_name.len() >= 2
                        && dir_name.len() <= 5
                        && dir_name.chars().all(|c| c.is_alphanumeric() || c == '-')
                    {
                        // Check if it has the expected structure (common.ftl or pages/ directory)
                        let has_common = path.join("common.ftl").exists();
                        let has_pages = path.join("pages").is_dir();

                        if has_common || has_pages {
                            languages.push(dir_name.to_string());
                        }
                    }
                }
            }
        }
    }

    // Sort for consistent ordering
    languages.sort();

    // Ensure we always have at least one language (default to 'en' if nothing
    // found)
    if languages.is_empty() {
        if debug_value > 0 {
            tracing::debug!("No languages discovered from locales directory, defaulting to 'en'");
        }
        languages.push("en".to_string());
    }

    if debug_value > 3 {
        tracing::debug!("Discovered available languages: {:?}", languages);
    }
    languages
}

/// Type alias for text container used throughout the application
pub type HashMapTexts = crate::defs::Texts;

/// Emergency fallback translations when all else fails - language agnostic
fn get_emergency_translations(available_languages: &[String]) -> HashMapTexts {
    let mut language_maps = Vec::new();

    for lang in available_languages {
        let mut texts = HashMap::new();

        // Default to English-like translations for unknown languages
        texts.insert("error".to_string(), "Error".to_string());
        texts.insert("loading".to_string(), "Loading...".to_string());
        texts.insert("home".to_string(), "Home".to_string());
        texts.insert("about".to_string(), "About".to_string());
        texts.insert("contact".to_string(), "Contact".to_string());

        language_maps.push((lang.clone(), texts));
    }

    crate::defs::Texts::from_language_maps(language_maps)
}

/// Load texts from FTL files for all available languages
/// Uses filesystem loading for server, embedded resources for WASM
pub fn load_texts_from_ftl(_primary_lang: &str) -> Result<HashMapTexts, Box<dyn Error>> {
    let debug_value = crate::get_debug_value();

    if cfg!(target_arch = "wasm32") {
        // In WASM, use embedded resources only - no filesystem access
        let available_languages = discover_languages_from_embedded_resources();

        if debug_value > 3 {
            tracing::debug!(
                "WASM: Using embedded resources for languages: {:?}",
                available_languages
            );
        }

        match load_texts_from_embedded_ftl(&available_languages) {
            Ok(texts) => {
                if debug_value > 3 {
                    tracing::debug!(
                        "WASM: Successfully loaded texts from embedded FTL resources with {} \
                         languages",
                        texts.translations.len()
                    );
                }
                Ok(texts)
            }
            Err(e) => {
                if debug_value > 0 {
                    tracing::debug!(
                        "WASM: Failed to load embedded FTL resources: {}, using emergency fallback",
                        e
                    );
                }
                // Final fallback with discovered languages
                Ok(get_emergency_translations(&available_languages))
            }
        }
    } else {
        // On server, use filesystem loading
        let root_path = get_root_path()?;
        let available_languages = discover_available_languages(&root_path);

        if debug_value > 3 {
            tracing::debug!(
                "Server: Using filesystem for languages: {:?}",
                available_languages
            );
        }

        // Try filesystem loading (server only)
        match load_texts_from_filesystem(&available_languages) {
            Ok(texts) => {
                if debug_value > 3 {
                    tracing::debug!(
                        "Server: Successfully loaded texts from filesystem FTL files: {} total \
                         languages with keys: {:?}",
                        texts.translations.len(),
                        texts
                            .translations
                            .iter()
                            .map(|(lang, keys)| format!("{}: {}", lang, keys.len()))
                            .collect::<Vec<_>>()
                            .join(", ")
                    );
                }
                Ok(texts)
            }
            Err(e) => {
                if debug_value > 0 {
                    tracing::debug!(
                        "Server: Filesystem loading failed: {}, trying embedded fallback",
                        e
                    );
                }

                // Try embedded resources as fallback (useful during development)
                match load_texts_from_embedded_ftl(&available_languages) {
                    Ok(texts) => {
                        if debug_value > 1 {
                            tracing::debug!(
                                "Server: Successfully loaded texts from embedded FTL resources \
                                 with {} languages",
                                texts.translations.len()
                            );
                        }
                        Ok(texts)
                    }
                    Err(embedded_err) => {
                        if debug_value > 0 {
                            tracing::debug!(
                                "Server: Failed to load embedded FTL resources: {}, using \
                                 emergency fallback",
                                embedded_err
                            );
                        }
                        // Final fallback with discovered languages
                        Ok(get_emergency_translations(&available_languages))
                    }
                }
            }
        }
    }
}

/// Get project root path for locating content
fn get_root_path() -> Result<PathBuf, Box<dyn Error>> {
    let debug_value = crate::get_debug_value();

    // Priority 1: Explicit project root environment variable
    if let Ok(project_root) = std::env::var("RUSTELO_PROJECT_ROOT") {
        let path = PathBuf::from(&project_root);
        if path.exists() {
            if debug_value > 2 {
                tracing::debug!("Using RUSTELO_PROJECT_ROOT: {}", project_root);
            }
            return Ok(path);
        } else {
            panic!(
                "RUSTELO_PROJECT_ROOT is set to '{}' but path does not exist",
                project_root
            );
        }
    }

    // Priority 2: ROOT_PATH environment variable (for compatibility)
    if let Ok(root_path) = std::env::var("ROOT_PATH") {
        let path = PathBuf::from(&root_path);
        if path.exists() {
            if debug_value > 2 {
                tracing::debug!("Using ROOT_PATH: {}", root_path);
            }
            return Ok(path);
        } else {
            panic!(
                "ROOT_PATH is set to '{}' but path does not exist",
                root_path
            );
        }
    }

    // Priority 3: Current working directory (most reliable)
    if let Ok(current_dir) = std::env::current_dir() {
        // Validate that this looks like a rustelo project by checking for site/
        // directory
        let site_dir = current_dir.join("site");
        let content_kinds_file = current_dir
            .join("site")
            .join("content")
            .join("content-kinds.toml");

        if site_dir.exists() || content_kinds_file.exists() {
            if debug_value > 2 {
                tracing::debug!(
                    "Using current working directory (validated): {}",
                    current_dir.display()
                );
            }
            return Ok(current_dir);
        } else if debug_value > 1 {
            tracing::debug!(
                "Current directory '{}' doesn't appear to be a rustelo project (no site/ \
                 directory found)",
                current_dir.display()
            );
        }
    }

    // Priority 4: PWD environment variable
    if let Ok(pwd) = std::env::var("PWD") {
        let path = PathBuf::from(&pwd);
        if path.exists() {
            let site_dir = path.join("site");
            if site_dir.exists() {
                if debug_value > 2 {
                    tracing::debug!("Using PWD (validated): {}", pwd);
                }
                return Ok(path);
            }
        }
    }

    // NO MORE CARGO_MANIFEST_DIR DEPENDENCY - it's unreliable in workspaces

    // Final error with clear instructions
    panic!(
        "Cannot determine project root path. Rustelo requires one of:\n1. Set \
         RUSTELO_PROJECT_ROOT environment variable to your project directory\n2. Set ROOT_PATH \
         environment variable to your project directory\n3. Run from a directory containing a \
         'site/' subdirectory\nCurrent working directory: {:?}\nAvailable environment variables: \
         PWD={:?}, ROOT_PATH={:?}, RUSTELO_PROJECT_ROOT={:?}",
        std::env::current_dir().unwrap_or_else(|_| PathBuf::from("<unknown>")),
        std::env::var("PWD").unwrap_or_else(|_| "<not set>".to_string()),
        std::env::var("ROOT_PATH").unwrap_or_else(|_| "<not set>".to_string()),
        std::env::var("RUSTELO_PROJECT_ROOT").unwrap_or_else(|_| "<not set>".to_string())
    );
}

/// Load modular FTL files from content/locales directory for any language
fn load_modular_ftl(locales_dir: &std::path::Path, lang: &str) -> Result<String, Box<dyn Error>> {
    let lang_dir = locales_dir.join(lang);
    let debug_value = crate::get_debug_value();
    if debug_value > 3 {
        tracing::debug!("Loading modular FTL for {}: {:?}", lang, lang_dir);
    }

    if !lang_dir.exists() {
        let error_msg = format!("Language directory not found: {}", lang_dir.display());
        if debug_value > 0 {
            tracing::debug!("{}", error_msg);
        }
        return Err(error_msg.into());
    }

    let mut combined_content = String::new();

    // Load common.ftl first
    let common_path = lang_dir.join("common.ftl");
    if common_path.exists() {
        combined_content.push_str(&std::fs::read_to_string(&common_path)?);
        combined_content.push('\n');
    }

    // Load auth.ftl
    let auth_path = lang_dir.join("auth.ftl");
    if auth_path.exists() {
        combined_content.push_str(&std::fs::read_to_string(&auth_path)?);
        combined_content.push('\n');
    }

    // Load all page FTL files
    let pages_dir = lang_dir.join("pages");
    if pages_dir.exists() {
        let entries = std::fs::read_dir(&pages_dir)?;
        for entry in entries {
            let entry = entry?;
            let path = entry.path();
            if path.extension().is_some_and(|ext| ext == "ftl") {
                combined_content.push_str(&std::fs::read_to_string(&path)?);
                combined_content.push('\n');
            }
        }
    }

    // Load all component FTL files
    let components_dir = lang_dir.join("components");
    if components_dir.exists() {
        let entries = std::fs::read_dir(&components_dir)?;
        for entry in entries {
            let entry = entry?;
            let path = entry.path();
            if path.extension().is_some_and(|ext| ext == "ftl") {
                combined_content.push_str(&std::fs::read_to_string(&path)?);
                combined_content.push('\n');
            }
        }
    }

    // Load all manager FTL files
    let manager_dir = lang_dir.join("manager");
    if manager_dir.exists() {
        if debug_value > 3 {
            tracing::debug!("Loading manager FTL files from: {:?}", manager_dir);
        }
        let entries = std::fs::read_dir(&manager_dir)?;
        for entry in entries {
            let entry = entry?;
            let path = entry.path();
            if path.extension().is_some_and(|ext| ext == "ftl") {
                if debug_value > 3 {
                    tracing::debug!("Loading manager FTL file: {:?}", path);
                }
                let content = std::fs::read_to_string(&path)?;
                if debug_value > 3 {
                    tracing::debug!("Manager FTL content length: {} chars", content.len());
                }
                combined_content.push_str(&content);
                combined_content.push('\n');
            }
        }
    } else if debug_value > 0 {
        tracing::debug!("Manager directory does not exist: {:?}", manager_dir);
    }

    if debug_value > 3 {
        tracing::debug!(
            "Loaded modular FTL for {}: {} characters",
            lang,
            combined_content.len()
        );
    }
    Ok(combined_content)
}

/// Load texts directly from filesystem FTL files for all available languages
fn load_texts_from_filesystem(
    available_languages: &[String],
) -> Result<HashMapTexts, Box<dyn Error>> {
    use std::collections::HashMap;

    use fluent_bundle::{FluentBundle, FluentResource};
    use unic_langid::LanguageIdentifier;

    let root_path = get_root_path()?;
    let debug_value = crate::get_debug_value();
    let site_i18n_path =
        std::env::var("SITE_I18N_PATH").unwrap_or_else(|_| "site/i18n".to_string());
    let locales_dir = root_path.join(&site_i18n_path).join("locales");

    if debug_value > 3 {
        tracing::debug!(
            "FTL loading: locales_dir = {:?}, exists = {}",
            locales_dir,
            locales_dir.exists()
        );
    }

    if !locales_dir.exists() {
        return Err(format!("Modular locales directory not found at {locales_dir:?}").into());
    }

    let mut language_maps = Vec::new();
    let mut all_keys = std::collections::HashSet::new();

    // Load content for each available language
    for lang in available_languages {
        if debug_value > 4 {
            tracing::debug!("Loading FTL content for language: {}", lang);
        }

        match load_modular_ftl(&locales_dir, lang) {
            Ok(content) => {
                if content.is_empty() {
                    debug!("No FTL content found for language: {}", lang);
                    continue;
                }

                // Create Fluent bundle for this language
                let langid: LanguageIdentifier = lang
                    .parse()
                    .unwrap_or_else(|_| "en".parse().expect("en should always be a valid locale"));
                let mut bundle = FluentBundle::new(vec![langid]);

                let resource = FluentResource::try_new(content)
                    .map_err(|_| format!("Failed to parse FTL for language: {}", lang))?;

                // Extract keys from the parsed Fluent resource instead of manual parsing
                for entry in resource.entries() {
                    if let ast::Entry::Message(message) = entry {
                        let key = message.id.name.to_string();
                        all_keys.insert(key);
                    }
                }

                bundle.add_resource(resource).map_err(|_| {
                    format!(
                        "Failed to add FTL resource to bundle for language: {}",
                        lang
                    )
                })?;

                // Extract translations for all discovered keys
                let mut lang_texts = HashMap::new();
                for key in &all_keys {
                    if let Some(message) = bundle.get_message(key) {
                        if let Some(pattern) = message.value() {
                            // Try to format without parameters first
                            let mut errors = Vec::new();
                            let value = bundle.format_pattern(pattern, None, &mut errors);

                            if errors.is_empty() {
                                // Successfully formatted without parameters
                                lang_texts.insert(key.clone(), value.to_string());
                            } else {
                                // Message has parameters, store the raw message for parameter
                                // substitution later We'll need to
                                // implement custom parameter substitution in the CLI
                                if debug_value > 4 {
                                    tracing::debug!(
                                        "Message '{}' has parameters, storing raw pattern",
                                        key
                                    );
                                }
                                // For now, let's just store a placeholder message that can be
                                // detected and handled specially in
                                // the CLI
                                let placeholder = format!("__FLUENT_MSG_WITH_PARAMS__:{}", key);
                                lang_texts.insert(key.clone(), placeholder);
                            }
                        }
                    }
                }

                if debug_value > 2 {
                    tracing::debug!(
                        "Loaded {} translations for language: {}",
                        lang_texts.len(),
                        lang
                    );
                }
                language_maps.push((lang.clone(), lang_texts));
            }
            Err(e) => {
                if debug_value > 0 {
                    tracing::debug!("Failed to load FTL content for language {}: {}", lang, e);
                }
            }
        }
    }

    if language_maps.is_empty() {
        return Err("No FTL content loaded for any language".into());
    }

    if debug_value > 2 {
        tracing::debug!(
            "Found {} total FTL keys across all languages",
            all_keys.len()
        );
    }
    Ok(crate::defs::Texts::from_language_maps(language_maps))
}

/// Load texts from embedded FTL resources (WASM-compatible) for any available
/// languages
fn load_texts_from_embedded_ftl(
    available_languages: &[String],
) -> Result<HashMapTexts, Box<dyn Error>> {
    use fluent_bundle::{FluentBundle, FluentResource};
    use fluent_syntax::ast;
    use tracing::warn;
    use unic_langid::LanguageIdentifier;

    use crate::resources::load;
    let debug_value = crate::get_debug_value();

    // Get all available FTL resources
    let available_ftl = load::available_ftl_resources();
    let mut language_maps = Vec::new();
    let mut all_message_ids = std::collections::HashSet::new();

    // Process each available language
    for lang in available_languages {
        let mut lang_texts: HashMap<String, String> = HashMap::new();

        // Create Fluent bundle for this language
        let langid: LanguageIdentifier = lang
            .parse()
            .unwrap_or_else(|_| "en".parse().expect("en should always be a valid locale"));
        let mut bundle = FluentBundle::new(vec![langid]);

        // Load FTL resources for this language (looking for resources that start with
        // "{lang}_")
        let lang_prefix = format!("{}_", lang);
        for resource_key in available_ftl
            .iter()
            .filter(|key| key.starts_with(&lang_prefix))
        {
            if let Ok(ftl_content) = load::ftl(resource_key) {
                match FluentResource::try_new(ftl_content.clone()) {
                    Ok(resource) => {
                        // Collect message IDs from this resource
                        for entry in resource.entries() {
                            if let ast::Entry::Message(message) = entry {
                                all_message_ids.insert(message.id.name.to_string());
                            }
                        }

                        if let Err(errors) = bundle.add_resource(resource) {
                            if debug_value > 0 {
                                warn!("Fluent bundle errors for {}: {:?}", resource_key, errors);
                            }
                        }
                    }
                    Err((_, errors)) => {
                        if debug_value > 0 {
                            warn!(
                                "Failed to parse FTL resource {}: {:?}",
                                resource_key, errors
                            );
                        }
                    }
                }
            }
        }

        // Extract translations for all discovered message IDs
        for message_id in &all_message_ids {
            if let Some(message) = bundle.get_message(message_id) {
                if let Some(pattern) = message.value() {
                    let mut errors = Vec::new();
                    let value = bundle.format_pattern(pattern, None, &mut errors);
                    if errors.is_empty() {
                        lang_texts.insert(message_id.to_string(), value.to_string());
                    }
                }
            }
        }

        if !lang_texts.is_empty() {
            if debug_value > 3 {
                tracing::debug!(
                    "Loaded {} embedded translations for language: {}",
                    lang_texts.len(),
                    lang
                );
            }
            language_maps.push((lang.clone(), lang_texts));
        }
    }

    // Ensure we have some content
    if language_maps.is_empty() {
        return Err("No FTL content loaded from embedded resources".into());
    }

    Ok(crate::defs::Texts::from_language_maps(language_maps))
}
