//! Internationalization (i18n) support using Fluent and TOML
//!
//! Provides multi-language support for forms, prompts, and messages.

mod loader;
mod resolver;

pub use loader::LocaleLoader;
pub use resolver::LocaleResolver;

use crate::error::{ErrorWrapper, Result};
use fluent::FluentArgs;
use fluent_bundle::{FluentBundle, FluentResource};
use std::collections::HashMap;
use unic_langid::LanguageIdentifier;

/// I18n bundle that combines Fluent translations and TOML translations
pub struct I18nBundle {
    bundle: FluentBundle<FluentResource>,
    fallback_bundle: FluentBundle<FluentResource>,
    toml_translations: HashMap<String, String>,
}

impl I18nBundle {
    /// Create a new I18nBundle from a locale and fallback locale
    pub fn new(
        locale: LanguageIdentifier,
        fallback_locale: LanguageIdentifier,
        loader: &LocaleLoader,
    ) -> Result<Self> {
        let bundle = Self::create_bundle(&locale, loader)?;
        let fallback_bundle = Self::create_bundle(&fallback_locale, loader)?;
        let toml_translations = loader.load_toml(&locale)?;

        Ok(Self {
            bundle,
            fallback_bundle,
            toml_translations,
        })
    }

    /// Create a FluentBundle for a given locale
    fn create_bundle(
        locale: &LanguageIdentifier,
        loader: &LocaleLoader,
    ) -> Result<FluentBundle<FluentResource>> {
        let mut bundle = FluentBundle::new(vec![locale.clone()]);

        for resource_str in loader.load_fluent(locale)? {
            let resource = FluentResource::try_new(resource_str)
                .map_err(|e| ErrorWrapper::i18n_failed(format!("Fluent parse error: {:?}", e)))?;
            bundle
                .add_resource(resource)
                .map_err(|e| ErrorWrapper::i18n_failed(format!("Bundle add error: {:?}", e)))?;
        }

        Ok(bundle)
    }

    /// Translate a message key
    ///
    /// Searches in order: main bundle → TOML translations → fallback bundle → missing key marker
    pub fn translate(&self, key: &str, args: Option<&FluentArgs<'_>>) -> String {
        // Try main bundle
        if let Some(msg) = self.bundle.get_message(key) {
            if let Some(pattern) = msg.value() {
                let mut errors = vec![];
                return self
                    .bundle
                    .format_pattern(pattern, args, &mut errors)
                    .to_string();
            }
        }

        // Try TOML translations
        if let Some(translation) = self.toml_translations.get(key) {
            return translation.clone();
        }

        // Try fallback bundle
        if let Some(msg) = self.fallback_bundle.get_message(key) {
            if let Some(pattern) = msg.value() {
                let mut errors = vec![];
                return self
                    .fallback_bundle
                    .format_pattern(pattern, args, &mut errors)
                    .to_string();
            }
        }

        // Return missing marker for debugging
        format!("[MISSING: {}]", key)
    }

    /// Check if a string looks like an i18n key
    ///
    /// Heuristic: contains dots or starts with lowercase letter
    pub fn is_i18n_key(text: &str) -> bool {
        text.contains('.') || text.chars().next().is_some_and(|c| c.is_lowercase())
    }

    /// Translate if the text looks like a key, otherwise return as-is
    pub fn translate_if_key(&self, text: &str, args: Option<&FluentArgs<'_>>) -> String {
        if Self::is_i18n_key(text) {
            self.translate(text, args)
        } else {
            text.to_string()
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_is_i18n_key() {
        assert!(I18nBundle::is_i18n_key("forms.title"));
        assert!(I18nBundle::is_i18n_key("registration"));
        assert!(!I18nBundle::is_i18n_key("Capitalized Text"));
        assert!(!I18nBundle::is_i18n_key("Mixed Case String"));
    }

    #[test]
    fn test_missing_translation() {
        let loader = LocaleLoader::new(std::path::PathBuf::from("locales"));
        // This will fail if locales dir doesn't exist, but that's OK for now
        if let Ok(bundle) =
            I18nBundle::new("en-US".parse().unwrap(), "en-US".parse().unwrap(), &loader)
        {
            let result = bundle.translate("nonexistent.key", None);
            assert!(result.contains("MISSING"));
        }
    }
}
