/// LocaleBundle — loads and caches translation strings.
///
/// A bundle holds all translation strings for one locale. Strings are
/// keyed by dot-delimited paths (e.g. "profile.followers"). The bundle
/// supports both flat strings and plural forms.

use std::collections::HashMap;
use crate::locale::Locale;
use crate::plural::{plural_form, PluralForm};

/// A single translation value — either a simple string or a plural map.
#[derive(Debug, Clone)]
pub enum TranslationValue {
    /// A simple translated string. May contain `{key}` interpolation placeholders.
    Simple(String),
    /// A plural-form map. Keys are form names: "zero", "one", "two", "few", "many", "other".
    Plural(HashMap<String, String>),
}

/// A bundle of translations for a single locale.
#[derive(Debug, Clone)]
pub struct LocaleBundle {
    pub locale: Locale,
    translations: HashMap<String, TranslationValue>,
}

impl LocaleBundle {
    /// Create an empty bundle for a locale.
    pub fn new(locale: Locale) -> Self {
        Self {
            locale,
            translations: HashMap::new(),
        }
    }

    /// Insert a simple translation.
    pub fn insert(&mut self, key: impl Into<String>, value: impl Into<String>) {
        self.translations.insert(
            key.into(),
            TranslationValue::Simple(value.into()),
        );
    }

    /// Insert a plural translation.
    pub fn insert_plural(
        &mut self,
        key: impl Into<String>,
        forms: HashMap<String, String>,
    ) {
        self.translations
            .insert(key.into(), TranslationValue::Plural(forms));
    }

    /// Look up a key and return the simple string (no interpolation).
    pub fn get_raw(&self, key: &str) -> Option<&str> {
        match self.translations.get(key)? {
            TranslationValue::Simple(s) => Some(s.as_str()),
            TranslationValue::Plural(_) => None,
        }
    }

    /// Look up a key with variable interpolation.
    ///
    /// Replaces `{name}` placeholders with values from `vars`.
    pub fn translate(&self, key: &str, vars: &HashMap<&str, String>) -> Option<String> {
        let raw = match self.translations.get(key)? {
            TranslationValue::Simple(s) => s.clone(),
            TranslationValue::Plural(forms) => {
                // For translate(), use "other" as default
                forms.get("other")?.clone()
            }
        };
        Some(interpolate(&raw, vars))
    }

    /// Look up a plural key with a count.
    ///
    /// Selects the correct plural form for the locale's language and count,
    /// then interpolates `{n}` and any other `vars`.
    pub fn translate_plural(
        &self,
        key: &str,
        count: i64,
        vars: &HashMap<&str, String>,
    ) -> Option<String> {
        let forms = match self.translations.get(key)? {
            TranslationValue::Plural(f) => f,
            TranslationValue::Simple(s) => {
                // Fall through: treat the simple string as "other"
                let mut result_vars = vars.clone();
                result_vars.insert("n", count.to_string());
                return Some(interpolate(s, &result_vars));
            }
        };

        let form = plural_form(&self.locale.language, count);
        let form_key = match form {
            PluralForm::Zero => "zero",
            PluralForm::One => "one",
            PluralForm::Two => "two",
            PluralForm::Few => "few",
            PluralForm::Many => "many",
            PluralForm::Other => "other",
        };

        let template = forms
            .get(form_key)
            .or_else(|| forms.get("other"))?;

        let mut result_vars = vars.clone();
        result_vars.insert("n", count.to_string());
        Some(interpolate(template, &result_vars))
    }

    /// Number of translations loaded.
    pub fn len(&self) -> usize {
        self.translations.len()
    }

    pub fn is_empty(&self) -> bool {
        self.translations.is_empty()
    }
}

/// Replace `{key}` placeholders in `template` with values from `vars`.
fn interpolate(template: &str, vars: &HashMap<&str, String>) -> String {
    let mut result = template.to_string();
    for (key, value) in vars {
        result = result.replace(&format!("{{{}}}", key), value);
    }
    result
}

/// Load a bundle from a TOML string.
///
/// Format:
/// ```toml
/// [profile]
/// follow = "Follow"
/// followers = { one = "{n} Follower", other = "{n} Followers" }
/// ```
pub fn load_toml(locale: Locale, toml_str: &str) -> Result<LocaleBundle, String> {
    let value: toml::Value = toml::from_str(toml_str)
        .map_err(|e| format!("TOML parse error: {}", e))?;

    let mut bundle = LocaleBundle::new(locale);

    if let toml::Value::Table(table) = value {
        load_table(&mut bundle, &table, "");
    }

    Ok(bundle)
}

fn load_table(bundle: &mut LocaleBundle, table: &toml::value::Table, prefix: &str) {
    for (key, value) in table {
        let full_key = if prefix.is_empty() {
            key.clone()
        } else {
            format!("{}.{}", prefix, key)
        };

        match value {
            toml::Value::String(s) => {
                bundle.insert(full_key, s.clone());
            }
            toml::Value::Table(inner) => {
                // Check if it's a plural table (has "one", "other", etc.)
                let is_plural = inner.contains_key("one")
                    || inner.contains_key("other")
                    || inner.contains_key("zero")
                    || inner.contains_key("few")
                    || inner.contains_key("many");

                if is_plural {
                    let mut forms = HashMap::new();
                    for (form, form_val) in inner {
                        if let toml::Value::String(s) = form_val {
                            forms.insert(form.clone(), s.clone());
                        }
                    }
                    bundle.insert_plural(full_key, forms);
                } else {
                    // Nested namespace
                    load_table(bundle, inner, &full_key);
                }
            }
            _ => {}
        }
    }
}

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

    fn en_bundle() -> LocaleBundle {
        let mut b = LocaleBundle::new(Locale::en_us());
        b.insert("profile.follow", "Follow");
        b.insert("profile.bio", "Bio");
        let mut forms = HashMap::new();
        forms.insert("one".to_string(), "{n} Follower".to_string());
        forms.insert("other".to_string(), "{n} Followers".to_string());
        b.insert_plural("profile.followers", forms);
        b
    }

    #[test]
    fn get_raw_simple() {
        let b = en_bundle();
        assert_eq!(b.get_raw("profile.follow"), Some("Follow"));
    }

    #[test]
    fn get_raw_missing() {
        let b = en_bundle();
        assert_eq!(b.get_raw("nonexistent.key"), None);
    }

    #[test]
    fn translate_simple() {
        let b = en_bundle();
        let vars = HashMap::new();
        assert_eq!(
            b.translate("profile.follow", &vars),
            Some("Follow".to_string())
        );
    }

    #[test]
    fn translate_plural_one() {
        let b = en_bundle();
        let vars = HashMap::new();
        assert_eq!(
            b.translate_plural("profile.followers", 1, &vars),
            Some("1 Follower".to_string())
        );
    }

    #[test]
    fn translate_plural_many() {
        let b = en_bundle();
        let vars = HashMap::new();
        assert_eq!(
            b.translate_plural("profile.followers", 42, &vars),
            Some("42 Followers".to_string())
        );
    }

    #[test]
    fn interpolation_replaces_placeholder() {
        let mut b = LocaleBundle::new(Locale::en_us());
        b.insert("greeting", "Hello, {name}!");
        let mut vars = HashMap::new();
        vars.insert("name", "Alice".to_string());
        assert_eq!(
            b.translate("greeting", &vars),
            Some("Hello, Alice!".to_string())
        );
    }

    #[test]
    fn bundle_len() {
        let b = en_bundle();
        assert_eq!(b.len(), 3);
    }

    #[test]
    fn load_toml_simple() {
        let toml = r#"
[profile]
follow = "Follow"
bio = "Bio"
"#;
        let bundle = load_toml(Locale::en_us(), toml).unwrap();
        assert_eq!(bundle.get_raw("profile.follow"), Some("Follow"));
        assert_eq!(bundle.get_raw("profile.bio"), Some("Bio"));
    }

    #[test]
    fn load_toml_plural() {
        let toml = r#"
[profile]
followers = { one = "{n} Follower", other = "{n} Followers" }
"#;
        let bundle = load_toml(Locale::en_us(), toml).unwrap();
        let vars = HashMap::new();
        assert_eq!(
            bundle.translate_plural("profile.followers", 1, &vars),
            Some("1 Follower".to_string())
        );
    }

    #[test]
    fn load_toml_invalid() {
        let result = load_toml(Locale::en_us(), "not valid toml %%%");
        assert!(result.is_err());
    }
}
