/// The t() translation function and LocaleContext.
///
/// Components call `ctx.t("key")` or `ctx.t_plural("key", count)`.
/// The context flows down from the experience root and carries the active bundle.

use std::collections::HashMap;
use crate::bundle::LocaleBundle;
use crate::locale::Locale;

/// The active localization context.
///
/// Holds the current locale and its translation bundle. Passed down
/// through the component tree. When the locale changes, the context
/// is updated and all components that used it re-render.
#[derive(Debug, Clone)]
pub struct LocaleContext {
    pub locale: Locale,
    bundle: LocaleBundle,
    /// Fallback bundle (typically English) used when a key is missing.
    fallback: Option<LocaleBundle>,
}

impl LocaleContext {
    /// Create a context with a locale and its bundle.
    pub fn new(locale: Locale, bundle: LocaleBundle) -> Self {
        Self {
            locale,
            bundle,
            fallback: None,
        }
    }

    /// Set a fallback bundle for missing keys.
    pub fn with_fallback(mut self, fallback: LocaleBundle) -> Self {
        self.fallback = Some(fallback);
        self
    }

    /// Translate a key with optional variable interpolation.
    ///
    /// Returns the key itself if not found (never panics).
    pub fn t(&self, key: &str) -> String {
        self.t_vars(key, &HashMap::new())
    }

    /// Translate a key with variables.
    pub fn t_vars(&self, key: &str, vars: &HashMap<&str, String>) -> String {
        // Try primary bundle
        if let Some(s) = self.bundle.translate(key, vars) {
            return s;
        }
        // Try fallback bundle
        if let Some(ref fb) = self.fallback {
            if let Some(s) = fb.translate(key, vars) {
                return s;
            }
        }
        // Return the key itself — visible but never panics
        key.to_string()
    }

    /// Translate a plural key with a count.
    pub fn t_plural(&self, key: &str, count: i64) -> String {
        self.t_plural_vars(key, count, &HashMap::new())
    }

    /// Translate a plural key with a count and extra variables.
    pub fn t_plural_vars(
        &self,
        key: &str,
        count: i64,
        vars: &HashMap<&str, String>,
    ) -> String {
        if let Some(s) = self.bundle.translate_plural(key, count, vars) {
            return s;
        }
        if let Some(ref fb) = self.fallback {
            if let Some(s) = fb.translate_plural(key, count, vars) {
                return s;
            }
        }
        key.to_string()
    }

    /// The current locale tag (e.g. "en-US").
    pub fn locale_tag(&self) -> String {
        self.locale.tag()
    }

    /// Whether the current locale is RTL.
    pub fn is_rtl(&self) -> bool {
        self.locale.is_rtl()
    }
}

/// Convenience macro for translation calls.
///
/// ```ignore
/// // Simple
/// let text = t!(ctx, "profile.follow");
///
/// // With variables
/// let text = t!(ctx, "greeting", name => "Alice");
///
/// // Plural
/// let text = t_n!(ctx, "profile.followers", count);
/// ```
#[macro_export]
macro_rules! t {
    ($ctx:expr, $key:expr) => {
        $ctx.t($key)
    };
    ($ctx:expr, $key:expr, $($var:ident => $val:expr),+) => {{
        let mut vars = std::collections::HashMap::new();
        $(vars.insert(stringify!($var), $val.to_string());)+
        $ctx.t_vars($key, &vars)
    }};
}

#[macro_export]
macro_rules! t_n {
    ($ctx:expr, $key:expr, $count:expr) => {
        $ctx.t_plural($key, $count as i64)
    };
    ($ctx:expr, $key:expr, $count:expr, $($var:ident => $val:expr),+) => {{
        let mut vars = std::collections::HashMap::new();
        $(vars.insert(stringify!($var), $val.to_string());)+
        $ctx.t_plural_vars($key, $count as i64, &vars)
    }};
}

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

    fn make_ctx() -> LocaleContext {
        let mut bundle = LocaleBundle::new(Locale::en_us());
        bundle.insert("profile.follow", "Follow");
        bundle.insert("greeting", "Hello, {name}!");
        let mut forms = HashMap::new();
        forms.insert("one".to_string(), "{n} Follower".to_string());
        forms.insert("other".to_string(), "{n} Followers".to_string());
        bundle.insert_plural("profile.followers", forms);
        LocaleContext::new(Locale::en_us(), bundle)
    }

    #[test]
    fn t_simple() {
        let ctx = make_ctx();
        assert_eq!(ctx.t("profile.follow"), "Follow");
    }

    #[test]
    fn t_missing_returns_key() {
        let ctx = make_ctx();
        assert_eq!(ctx.t("no.such.key"), "no.such.key");
    }

    #[test]
    fn t_vars_interpolation() {
        let ctx = make_ctx();
        let mut vars = HashMap::new();
        vars.insert("name", "Bob".to_string());
        assert_eq!(ctx.t_vars("greeting", &vars), "Hello, Bob!");
    }

    #[test]
    fn t_plural_one() {
        let ctx = make_ctx();
        assert_eq!(ctx.t_plural("profile.followers", 1), "1 Follower");
    }

    #[test]
    fn t_plural_many() {
        let ctx = make_ctx();
        assert_eq!(ctx.t_plural("profile.followers", 42), "42 Followers");
    }

    #[test]
    fn is_rtl_english() {
        let ctx = make_ctx();
        assert!(!ctx.is_rtl());
    }

    #[test]
    fn is_rtl_arabic() {
        let bundle = LocaleBundle::new(Locale::ar());
        let ctx = LocaleContext::new(Locale::ar(), bundle);
        assert!(ctx.is_rtl());
    }

    #[test]
    fn fallback_bundle_used() {
        let primary_bundle = LocaleBundle::new(Locale::new("fr"));
        // Key not in French bundle
        let mut fallback = LocaleBundle::new(Locale::en_us());
        fallback.insert("app.name", "My App");
        let ctx = LocaleContext::new(Locale::new("fr"), primary_bundle)
            .with_fallback(fallback);
        assert_eq!(ctx.t("app.name"), "My App");
    }

    #[test]
    fn locale_tag() {
        let ctx = make_ctx();
        assert_eq!(ctx.locale_tag(), "en-US");
    }
}
