//! Reverse transliteration: romanized Latin text → native script.
//!
//! Given a romanized string and a target language, converts back to the
//! native script using longest-match greedy scanning to handle digraphs
//! and trigraphs (e.g., "sh" → ш, "shch" → щ).

#![allow(clippy::unreadable_literal)]

use pyo3::prelude::*;

// Reverse transliteration tables — generated by build.rs from TSV data.
include!(concat!(env!("OUT_DIR"), "/reverse_translit_phf.rs"));

/// Maximum key length in any reverse table (for greedy matching window).
const MAX_KEY_LEN: usize = 4; // "shch" is the longest key

/// Look up a romanized substring in the appropriate reverse table.
fn reverse_lookup<'a>(key: &str, lang: &str) -> Option<&'a str> {
    let table: &phf::Map<&'static str, &'static str> = match lang {
        "ru" => &REVERSE_RU,
        "uk" => &REVERSE_UK,
        "el" => &REVERSE_EL,
        _ => return None,
    };
    table.get(key).copied()
}

/// Languages that support reverse transliteration.
const REVERSE_LANGS: &[&str] = &["el", "ru", "uk"];

/// Check if reverse transliteration is supported for a language.
pub fn supports_reverse(lang: &str) -> bool {
    REVERSE_LANGS.contains(&lang)
}

/// Return list of languages supporting reverse transliteration.
pub fn reverse_langs() -> Vec<String> {
    REVERSE_LANGS.iter().map(|s| (*s).to_string()).collect()
}

/// Core reverse transliteration: romanized ASCII → native script.
///
/// Uses greedy longest-match scanning: for each position in the input,
/// tries the longest possible key first (up to MAX_KEY_LEN characters),
/// falling back to shorter keys, and finally passing through unmatched
/// characters verbatim.
pub fn reverse_transliterate_impl(text: &str, lang: &str) -> String {
    let bytes = text.as_bytes();
    let len = bytes.len();
    let mut result = String::with_capacity(len);
    let mut i = 0;

    while i < len {
        let remaining = len - i;
        let max_try = remaining.min(MAX_KEY_LEN);
        let mut matched = false;

        // Greedy: try longest key first, work down to single char
        for key_len in (1..=max_try).rev() {
            // Only try if the slice is valid UTF-8 (it always will be for ASCII input,
            // but guard against partial multi-byte sequences)
            if let Ok(candidate) = std::str::from_utf8(&bytes[i..i + key_len]) {
                if let Some(native) = reverse_lookup(candidate, lang) {
                    result.push_str(native);
                    i += key_len;
                    matched = true;
                    break;
                }
            }
        }

        if !matched {
            // Pass through unmatched character verbatim
            // Handle potential multi-byte UTF-8 properly
            if let Some(ch) = text[i..].chars().next() {
                result.push(ch);
                i += ch.len_utf8();
            } else {
                i += 1;
            }
        }
    }

    result
}

/// PyO3 entry point for reverse transliteration.
#[pyfunction]
#[pyo3(signature = (text, *, lang))]
pub fn _reverse_transliterate(text: &str, lang: &str) -> PyResult<String> {
    if !supports_reverse(lang) {
        let available = REVERSE_LANGS.join(", ");
        return Err(pyo3::exceptions::PyValueError::new_err(format!(
            "reverse transliteration not supported for lang={lang:?}; available: {available}"
        )));
    }
    Ok(reverse_transliterate_impl(text, lang))
}

/// PyO3 entry point: list languages that support reverse transliteration.
#[pyfunction]
pub fn _reverse_langs() -> Vec<String> {
    reverse_langs()
}

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

    #[test]
    fn test_russian_basic() {
        assert_eq!(reverse_transliterate_impl("Moskva", "ru"), "Москва");
    }

    #[test]
    fn test_russian_digraphs() {
        assert_eq!(reverse_transliterate_impl("zh", "ru"), "ж");
        assert_eq!(reverse_transliterate_impl("sh", "ru"), "ш");
        assert_eq!(reverse_transliterate_impl("ch", "ru"), "ч");
    }

    #[test]
    fn test_russian_trigraphs() {
        assert_eq!(reverse_transliterate_impl("shch", "ru"), "щ");
    }

    #[test]
    fn test_passthrough() {
        assert_eq!(reverse_transliterate_impl("123!", "ru"), "123!");
    }

    #[test]
    fn test_supports_reverse() {
        assert!(supports_reverse("ru"));
        assert!(supports_reverse("uk"));
        assert!(supports_reverse("el"));
        assert!(!supports_reverse("de"));
    }
}
