use std::collections::HashMap;

use tracing::debug;

use crate::Result;

/// 本地化管理器，支持多语言文本的注册和动态翻译。
///
/// 维护一个从语言标识到键值对映射的嵌套哈希表。
/// 语言标识使用 `en_us`、`zh_cn` 等格式，键为翻译键，值为包含占位符 `{}` 的模板字符串。
/// 翻译时支持按顺序替换占位符，适用于动态填充内容（如用户名、数字等）。
/// 当指定语言中找不到翻译键时，会尝试使用备用语言（fallback）进行查找。
pub struct LocalizationManager {
    translations: HashMap<String, HashMap<String, String>>,
    fallback_language: String,
}

impl LocalizationManager {
    /// 创建一个新的 `LocalizationManager`，翻译表为空，默认备用语言为 `"en_us"`。
    pub fn new() -> Self {
        Self {
            translations: HashMap::new(),
            fallback_language: "en_us".to_string(),
        }
    }

    /// 设置 fallback 语言标识。
    ///
    /// 当使用 `translate` 时，如果指定语言找不到对应键，会尝试使用此 fallback 语言。
    /// 如果 fallback 语言也找不到，则返回原始键。
    ///
    /// # 参数
    /// - `lang`: 新的 fallback 语言标识（如 `"zh_cn"`）。
    pub fn set_fallback_language(&mut self, lang: &str) {
        self.fallback_language = lang.to_string();
    }

    /// 批量注册指定语言的翻译文本。
    ///
    /// 该方法接收一个 JSON 字符串，该字符串必须是一个对象，其成员为键值对（键和值均为字符串）。
    /// 注册的翻译会合并到对应语言的现有翻译中（如果之前已注册过相同键，则覆盖）。
    ///
    /// # 参数
    /// - `lang`: 语言标识，例如 `"en_us"`、`"zh_cn"`。
    /// - `json_content`: 符合格式的 JSON 字符串，示例：`{"greeting":"Hello, {}!","farewell":"Goodbye"}`。
    pub fn register_translations(&mut self, lang: &str, json_content: &str) -> Result<()> {
        let entries: HashMap<String, String> = serde_json::from_str(json_content)?;
        let count = entries.len();
        let lang_map = self
            .translations
            .entry(lang.to_string())
            .or_insert_with(HashMap::new);
        lang_map.extend(entries);
        debug!(lang, count, "translations registered");
        Ok(())
    }

    /// 检查指定语言是否已注册翻译。
    pub fn has_language(&self, lang: &str) -> bool {
        self.translations.contains_key(lang)
    }

    /// 返回所有已注册的语言标识列表。
    pub fn list_languages(&self) -> Vec<String> {
        self.translations.keys().cloned().collect()
    }

    /// 根据语言、翻译键以及可选参数获取翻译后的文本。
    ///
    /// 该方法会查找指定语言下对应键的模板字符串，然后将 `format_args` 中的参数**按顺序**替换模板中的 `{}` 占位符。
    /// 如果语言不存在或键不存在，则尝试使用 fallback 语言进行查找。
    /// 如果仍然找不到，则直接返回原始键。
    /// 如果参数数量少于占位符数量，多余的 `{}` 将保留原样；如果参数数量多于占位符数量，多余参数将被忽略。
    ///
    /// # 参数
    /// - `lang`: 目标语言标识。
    /// - `key`: 翻译键。
    /// - `format_args`: 要填充到占位符中的字符串切片列表，按顺序替换。
    ///
    /// # 返回值
    /// 翻译后的字符串（若找到模板），否则返回原始键。
    pub fn translate(&self, lang: &str, key: &str, format_args: &[&str]) -> String {
        let template = self
            .translations
            .get(lang)
            .and_then(|map| map.get(key))
            .or_else(|| {
                let fallback = self
                    .translations
                    .get(&self.fallback_language)
                    .and_then(|map| map.get(key));
                if fallback.is_some() {
                    debug!(lang, fallback = %self.fallback_language, key, "using fallback translation");
                }
                fallback
            });

        match template {
            Some(template) => {
                let mut result = template.to_string();
                for arg in format_args {
                    if let Some(pos) = result.find("{}") {
                        result.replace_range(pos..pos + 2, arg);
                    } else {
                        break;
                    }
                }
                result
            }
            None => {
                debug!(lang, key, "translation not found, returning key");
                key.to_string()
            }
        }
    }
}

impl Default for LocalizationManager {
    fn default() -> Self {
        Self::new()
    }
}
