Documentation
use fluent::FluentArgs;
use intl_memoizer::concurrent::IntlLangMemoizer;
use once_cell::sync::Lazy;
use std::sync::RwLock;
use std::{collections::HashMap, sync::Arc};

use fluent::{bundle::FluentBundle, FluentResource};

type SharedFluentBundle = Arc<FluentBundle<FluentResource, IntlLangMemoizer>>;

static TRANSLATIONS: Lazy<RwLock<HashMap<String, SharedFluentBundle>>> =
  Lazy::new(|| RwLock::new(HashMap::new()));

pub fn load_translations_from_text(language: impl AsRef<str>, content: impl AsRef<str>) -> Result<(), String> {
  let resource = match FluentResource::try_new(content.as_ref().to_string()) {
    Ok(v) => v,
    Err(e) => {
      return Err(format!("{:?}", e));
    },
  };

  let language_ref = language.as_ref();
  let lang_id = match language_ref.parse() {
    Ok(v) => v,
    Err(e) => {
      return Err(format!("{:?}", e));
    },
  };
  let mut bundle = FluentBundle::new_concurrent(vec![lang_id]);
  if let Err(e) = bundle.add_resource(resource) {
    return Err(format!("{:?}", e));
  }
  let mut translations_map = TRANSLATIONS.write().unwrap();
  translations_map.insert(language_ref.to_string(), Arc::new(bundle));
  Ok(())
}

pub fn translate(
  language: impl AsRef<str>,
  fallback_language: impl AsRef<str>,
  key: &str,
  args: &FluentArgs,
) -> String {
  let language = language.as_ref();
  let fallback_language = fallback_language.as_ref();
  if language.is_empty() && fallback_language.is_empty() {
    return String::default();
  }
  let language = if language.is_empty() { fallback_language } else { language };

  let mut translated = extract_translate(language, key, args);

  if translated.is_empty() {
    translated = extract_translate(fallback_language, key, args);
  }
  translated
}

fn extract_translate(language: impl AsRef<str>, key: &str, args: &FluentArgs) -> String {
  let translations = TRANSLATIONS.read().unwrap();
  let language_ref = language.as_ref();
  if let Some(bundle) = translations.get(language_ref) {
    if let Some(msg) = bundle.get_message(key) {
      if let Some(pattern) = msg.value() {
        let mut errors = vec![];
        let value = bundle.format_pattern(pattern, Some(args), &mut errors);
        return value.to_string();
      }
    }
  }
  String::default()
}

pub fn languages() -> Vec<String> {
  let translations = TRANSLATIONS.read().unwrap();
  translations.keys().cloned().collect()
}