use hmac::{Hmac, Mac};
use sha2::Sha256;
use serde::{Deserialize, Serialize};
use color_eyre::Result;
use crate::HashMap;
use std::time::SystemTime;
use std::sync::RwLock;
use std::sync::Arc;
use std::thread;
use std::time::Duration;
use crate::repository::api;

#[derive(Debug, Clone, Serialize)]
pub struct TranslationUsage {
    pub id: String,
    pub timestamp: SystemTime,
}

#[derive(Debug)]
pub struct Translations {
    pub dictionary: HashMap<Language, HashMap<String, String>>,
    pub last_updated: SystemTime,
    pub preloaded: bool,
    pub update_interval: Duration,
}

impl Default for Translations {
    fn default() -> Self {
        Self {
            dictionary: HashMap::default(),
            last_updated: SystemTime::UNIX_EPOCH,
            preloaded: false,
            update_interval: Duration::from_secs(5 * 60), // 5 minutes default
        }
    }
}

#[derive(Serialize, Deserialize, Clone, Default, Debug, PartialEq, Eq, Copy, Hash, strum::Display)]
pub enum Language {
    #[default]
    #[serde(rename = "rus")]
    #[strum(serialize = "rus")]
    Ru,
    #[serde(rename = "eng")]
    #[strum(serialize = "eng")]
    En,
}

#[allow(dead_code)]
#[derive(Clone)]
pub enum Uniq {
    Module(&'static str),
    Manual(&'static str),
}

/// Global translation dictionary - users can initialize this with their own API implementation
pub static TRANSLATION_DICTIONARY: RwLock<Option<Translations>> = RwLock::new(None);

/// Background update task handle
static UPDATE_TASK: RwLock<Option<Arc<()>>> = RwLock::new(None);

/// Initialize the translation dictionary
/// 
/// This function should be called before using any translation functions.
/// It sets up the initial empty dictionary structure.
pub fn init_translation_dictionary() -> Result<()> {
    let mut dict = TRANSLATION_DICTIONARY.write().unwrap();
    *dict = Some(Translations::default());
    Ok(())
}

/// Start the background dictionary update task
/// 
/// This function spawns a background thread that periodically updates the translation
/// dictionary from the remote API. The update interval is configurable via
/// `set_update_interval()`.
/// 
/// # Example
/// ```rust
/// use rust_shared::i18n::{init_translation_dictionary, start_dictionary_update_task};
/// 
/// // Initialize the dictionary
/// init_translation_dictionary()?;
/// 
/// // Start background updates every 5 minutes
/// start_dictionary_update_task()?;
/// ```
pub fn start_dictionary_update_task() -> Result<()> {
    let update_interval = {
        let dict = TRANSLATION_DICTIONARY.read().unwrap();
        dict.as_ref()
            .map(|d| d.update_interval)
            .unwrap_or(Duration::from_secs(5 * 60))
    };

    let task_handle = Arc::new(());
    let task_handle_clone = task_handle.clone();
    
    thread::spawn(move || {
        let _handle = task_handle_clone;
        
        let rt = tokio::runtime::Builder::new_current_thread()
            .enable_all()
            .build()
            .unwrap();
        
        loop {
            thread::sleep(update_interval);
            
            if let Err(e) = rt.block_on(update_translation_dictionary()) {
                log::warn!("Failed to update translation dictionary: {}", e);
            }
        }
    });

    let mut update_task = UPDATE_TASK.write().unwrap();
    *update_task = Some(task_handle);
    
    Ok(())
}

/// Stop the background dictionary update task
/// 
/// This function stops the background thread that was updating the dictionary.
/// The dictionary will no longer be automatically updated.
pub fn stop_dictionary_update_task() {
    let mut update_task = UPDATE_TASK.write().unwrap();
    *update_task = None;
}

/// Preload all translations found by get_all_translation_keys function
/// 
/// This function should be called with the output of the build script to bulk-load
/// all translations into the dictionary. This is an async function that performs
/// HTTP requests to load translations.
/// 
/// # Example
/// ```rust
/// use rust_shared::i18n::preload_translations_from_keys;
/// 
/// #[tokio::main]
/// async fn main() -> Result<()> {
///     // Preload all translations from build script
///     preload_translations_from_keys(get_all_translation_keys).await?;
///     Ok(())
/// }
/// ```
pub async fn preload_translations_from_keys<F>(get_keys_fn: F) -> Result<()> 
where
    F: Fn() -> Vec<(String, Uniq, Language)>,
{
    log::info!("Preloading all translations...");
    let keys = get_keys_fn();
    
    let mut translations_to_load = Vec::new();
    
    for (text, uniq, text_language) in keys {
        let (id, label) = match uniq {
            Uniq::Module(module) => (hash_string(&text, module), module.to_string()),
            Uniq::Manual(uniq) => (hash_string(&text, uniq), uniq.to_string()),
        };

        translations_to_load.push((id.clone(), label.clone(), text.clone(), text_language));
    }
    
    if !translations_to_load.is_empty() {
        log::info!("Bulk loading {} translation entries...", translations_to_load.len());
        api::bulk_load_translations(&translations_to_load).await?;
    }
    
    update_translation_dictionary().await?;
    {
        let mut translation_dictionary = TRANSLATION_DICTIONARY.write().unwrap();
        if let Some(ref mut dict) = *translation_dictionary {
            dict.preloaded = true;
        }
    }
    
    log::info!("Preloading complete!");
    Ok(())
}

/// Update the translation dictionary from the remote API
/// 
/// This function fetches the latest translations from the remote API and updates
/// the local dictionary cache. This is an async function that performs HTTP requests.
/// 
/// This function is typically called automatically by the background update task,
/// but can also be called manually if needed.
pub async fn update_translation_dictionary() -> Result<()> {
    let mut translation_dictionary = TRANSLATION_DICTIONARY.write().unwrap();
    if let Some(ref mut dict) = *translation_dictionary {
        dict.dictionary.insert(Language::En, api::get_translation_dictionary(Language::En).await?);
        dict.dictionary.insert(Language::Ru, api::get_translation_dictionary(Language::Ru).await?);
        dict.last_updated = SystemTime::now();
    }
    Ok(())
}


/// Set the update interval for the dictionary
/// 
/// This function allows you to configure how often the background task should
/// update the dictionary from the remote API.
/// 
/// # Example
/// ```rust
/// use rust_shared::i18n::set_update_interval;
/// use std::time::Duration;
/// 
/// // Update every 10 minutes instead of the default 5 minutes
/// set_update_interval(Duration::from_secs(10 * 60));
/// ```
pub fn set_update_interval(interval: Duration) {
    let mut translation_dictionary = TRANSLATION_DICTIONARY.write().unwrap();
    if let Some(ref mut dict) = *translation_dictionary {
        dict.update_interval = interval;
    }
}

/// Main translation function - now purely synchronous, no HTTP calls
/// 
/// This function translates text based on the current language and unique identifier.
/// It only looks up translations in the local dictionary cache and never makes
/// HTTP requests. This ensures it's fast and can be used in WASM environments.
/// 
/// The function returns a `&'static str` which means the caller owns the process lifetime.
/// 
/// # Example
/// ```rust
/// use rust_shared::i18n::{trans, Language, Uniq};
/// 
/// let translated = trans("Hello", Uniq::Module("my_module"), Language::En, &Language::Ru);
/// ```
pub fn trans(text: impl Into<String>, uniq: Uniq, text_language: Language, language: &Language) -> &'static str {
    let text = text.into();
    let (id, _label) = match uniq {
        Uniq::Module(module) => (hash_string(&text, module), module.to_string()),
        Uniq::Manual(uniq)   => (hash_string(&text, uniq),   uniq.to_string()),
    };

    log::trace!("trans: looking up '{}' (id: {}) for language {:?}", text, id, language);

    // 1) Ensure dictionary is initialized (avoid holding a lock across init)
    {
        let need_init = {
            let r = TRANSLATION_DICTIONARY.read().unwrap();
            r.is_none()
        };
        if need_init {
            log::debug!("trans: initializing dictionary for '{}'", text);
            let _ = init_translation_dictionary();
        }
    }

    // 2) Try dictionary lookup (short-lived read lock)
    let maybe_translated = {
        let r = TRANSLATION_DICTIONARY.read().unwrap();
        let dict = r.as_ref().unwrap();
        
        log::trace!("trans: dictionary has {} languages", dict.dictionary.len());
        if let Some(lang_map) = dict.dictionary.get(language) {
            log::trace!("trans: language {:?} has {} translations", language, lang_map.len());
            if let Some(translation) = lang_map.get(&id) {
                log::trace!("trans: found translation for '{}': '{}'", text, translation);
                Some(translation.clone())
            } else {
                log::trace!("trans: no translation found for id '{}' in language {:?}", id, language);
                None
            }
        } else {
            log::trace!("trans: no translations for language {:?}", language);
            None
        }
    };

    let translated_text = if let Some(t) = maybe_translated {
        log::debug!("trans: '{}' -> '{}'", text, t);
        t
    } else {
        log::warn!(
            "translation not found (\n\tid: {id},\n\ttext: {text},\n\ttext_language: {text_language},\n\tlanguage: {language}\n)"
        );
        text
    };

    // 3) Leak to &'static str as requested by API (caller owns process lifetime)
    Box::leak(translated_text.into_boxed_str())
}

fn hash_string(input: &str, key: &str) -> String {
    let mut mac = Hmac::<Sha256>::new_from_slice(key.as_bytes())
        .expect("HMAC can take key of any size");
    mac.update(input.as_bytes());
    let result = mac.finalize().into_bytes();
    hex::encode(result)
}
