//! Currency symbol, code, and alias resolution.
//!
//! Resolves currency symbols ($, EUR, R$), ISO 4217 codes (USD, EUR, GBP),
//! natural-language aliases (dollars, euros, pounds), and cryptocurrency
//! symbols (BTC, ETH) to their canonical identifiers.

use crate::currency::crypto;

// ---------------------------------------------------------------------------
// Symbol -> ISO code
// ---------------------------------------------------------------------------

/// Resolve a currency symbol string (e.g., "$", "R$") to its ISO 4217 code.
pub fn resolve_symbol(symbol: &str) -> Option<&'static str> {
    match symbol {
        "$" | "US$" => Some("USD"),
        "€" => Some("EUR"),
        "£" => Some("GBP"),
        "¥" => Some("JPY"),
        "R$" => Some("BRL"),
        "₹" => Some("INR"),
        "₩" => Some("KRW"),
        "₽" => Some("RUB"),
        "₺" => Some("TRY"),
        "₴" => Some("UAH"),
        "₱" => Some("PHP"),
        "฿" => Some("THB"),
        "₫" => Some("VND"),
        "₦" => Some("NGN"),
        "₡" => Some("CRC"),
        "₵" => Some("GHS"),
        "₸" => Some("KZT"),
        "₮" => Some("MNT"),
        "₪" => Some("ILS"),
        "kr" => Some("SEK"), // ambiguous, default to SEK
        "C$" => Some("CAD"),
        "A$" => Some("AUD"),
        "NZ$" => Some("NZD"),
        "HK$" => Some("HKD"),
        "S$" => Some("SGD"),
        "NT$" => Some("TWD"),
        "MX$" => Some("MXN"),
        "zl" | "zł" => Some("PLN"),
        "Ft" => Some("HUF"),
        "Kc" | "Kč" => Some("CZK"),
        "Rp" => Some("IDR"),
        "RM" => Some("MYR"),
        "CHF" => Some("CHF"), // symbol == code for Swiss franc
        _ => None,
    }
}

// ---------------------------------------------------------------------------
// Alias -> ISO code (natural language)
// ---------------------------------------------------------------------------

/// Resolve a natural-language alias to its ISO 4217 code (case-insensitive).
pub fn resolve_alias(alias: &str) -> Option<&'static str> {
    match alias.to_lowercase().as_str() {
        "dollar" | "dollars" | "buck" | "bucks" => Some("USD"),
        "euro" | "euros" => Some("EUR"),
        "pound" | "pounds" | "quid" => Some("GBP"),
        "yen" => Some("JPY"),
        "yuan" | "renminbi" | "rmb" => Some("CNY"),
        "real" | "reais" => Some("BRL"),
        "rupee" | "rupees" => Some("INR"),
        "franc" | "francs" => Some("CHF"),
        "krona" | "kronor" => Some("SEK"),
        "krone" | "kroner" => Some("NOK"),
        "won" => Some("KRW"),
        "lira" => Some("TRY"),
        "ruble" | "rubles" | "rouble" | "roubles" => Some("RUB"),
        "ringgit" => Some("MYR"),
        "baht" => Some("THB"),
        "peso" | "pesos" => Some("MXN"),
        "rand" => Some("ZAR"),
        "shekel" | "shekels" => Some("ILS"),
        "zloty" => Some("PLN"),
        "forint" => Some("HUF"),
        "koruna" => Some("CZK"),
        "dirham" | "dirhams" => Some("AED"),
        "riyal" | "riyals" => Some("SAR"),
        "bitcoin" | "btc" | "satoshi" | "sats" => Some("BTC"),
        "ether" | "ethereum" => Some("ETH"),
        _ => None,
    }
}

// ---------------------------------------------------------------------------
// ISO 4217 code validation (fiat)
// ---------------------------------------------------------------------------

/// Comprehensive set of recognized fiat ISO 4217 currency codes.
/// This includes 180+ currencies actively traded or in circulation.
pub fn is_currency_code(code: &str) -> bool {
    matches!(
        code,
        // Major / G10
        "USD" | "EUR" | "GBP" | "JPY" | "CHF" | "CAD" | "AUD" | "NZD"
        // Asia
        | "CNY" | "HKD" | "SGD" | "TWD" | "KRW" | "INR" | "PKR"
        | "BDT" | "LKR" | "NPR" | "THB" | "MYR" | "IDR" | "PHP"
        | "VND" | "KHR" | "LAK" | "MMK" | "BND" | "MVR" | "MNT"
        // Middle East
        | "TRY" | "ILS" | "AED" | "SAR" | "QAR" | "BHD" | "OMR"
        | "KWD" | "JOD" | "LBP" | "IQD" | "IRR" | "YER" | "SYP"
        // Eastern Europe / CIS
        | "RUB" | "PLN" | "CZK" | "HUF" | "RON" | "BGN" | "HRK"
        | "ISK" | "UAH" | "RSD" | "BAM" | "MKD" | "ALL" | "MDL"
        | "GEL" | "AMD" | "AZN" | "BYN"
        // Scandinavia
        | "SEK" | "NOK" | "DKK"
        // Americas
        | "MXN" | "BRL" | "ARS" | "CLP" | "COP" | "PEN" | "UYU"
        | "PYG" | "BOB" | "VES" | "CRC" | "GTQ" | "HNL" | "NIO"
        | "PAB" | "DOP" | "JMD" | "TTD" | "HTG" | "BBD" | "BSD"
        | "BZD" | "GYD" | "SRD" | "AWG" | "ANG" | "BMD" | "KYD"
        | "CUP" | "CUC" | "XCD"
        // Africa
        | "ZAR" | "EGP" | "NGN" | "KES" | "GHS" | "TZS" | "UGX"
        | "ETB" | "MAD" | "TND" | "DZD" | "LYD" | "XOF" | "XAF"
        | "CDF" | "AOA" | "MZN" | "ZMW" | "BWP" | "MWK" | "RWF"
        | "SOS" | "SDG" | "SCR" | "MUR" | "GMD" | "SLL" | "GNF"
        | "CVE" | "NAD" | "SZL" | "LSL" | "BIF" | "DJF" | "ERN"
        | "STN" | "KMF" | "MGA"
        // Pacific
        | "FJD" | "PGK" | "WST" | "TOP" | "VUV" | "SBD"
        // Central Asia
        | "KZT" | "UZS" | "KGS" | "TJS" | "TMT"
        // Other / special
        | "AFN" | "BTN" | "XDR" | "XAU" | "XAG"
    )
}

/// Return a `&'static str` for a validated fiat currency code.
fn resolve_code_static(code: &str) -> Option<&'static str> {
    // This is a macro-like approach to avoid repeating the giant list.
    // We match every code we know and return its static reference.
    match code {
        "USD" => Some("USD"), "EUR" => Some("EUR"), "GBP" => Some("GBP"),
        "JPY" => Some("JPY"), "CHF" => Some("CHF"), "CAD" => Some("CAD"),
        "AUD" => Some("AUD"), "NZD" => Some("NZD"), "CNY" => Some("CNY"),
        "HKD" => Some("HKD"), "SGD" => Some("SGD"), "TWD" => Some("TWD"),
        "KRW" => Some("KRW"), "INR" => Some("INR"), "PKR" => Some("PKR"),
        "BDT" => Some("BDT"), "LKR" => Some("LKR"), "NPR" => Some("NPR"),
        "THB" => Some("THB"), "MYR" => Some("MYR"), "IDR" => Some("IDR"),
        "PHP" => Some("PHP"), "VND" => Some("VND"), "KHR" => Some("KHR"),
        "LAK" => Some("LAK"), "MMK" => Some("MMK"), "BND" => Some("BND"),
        "MVR" => Some("MVR"), "MNT" => Some("MNT"), "TRY" => Some("TRY"),
        "ILS" => Some("ILS"), "AED" => Some("AED"), "SAR" => Some("SAR"),
        "QAR" => Some("QAR"), "BHD" => Some("BHD"), "OMR" => Some("OMR"),
        "KWD" => Some("KWD"), "JOD" => Some("JOD"), "LBP" => Some("LBP"),
        "IQD" => Some("IQD"), "IRR" => Some("IRR"), "YER" => Some("YER"),
        "SYP" => Some("SYP"), "RUB" => Some("RUB"), "PLN" => Some("PLN"),
        "CZK" => Some("CZK"), "HUF" => Some("HUF"), "RON" => Some("RON"),
        "BGN" => Some("BGN"), "HRK" => Some("HRK"), "ISK" => Some("ISK"),
        "UAH" => Some("UAH"), "RSD" => Some("RSD"), "BAM" => Some("BAM"),
        "MKD" => Some("MKD"), "ALL" => Some("ALL"), "MDL" => Some("MDL"),
        "GEL" => Some("GEL"), "AMD" => Some("AMD"), "AZN" => Some("AZN"),
        "BYN" => Some("BYN"), "SEK" => Some("SEK"), "NOK" => Some("NOK"),
        "DKK" => Some("DKK"), "MXN" => Some("MXN"), "BRL" => Some("BRL"),
        "ARS" => Some("ARS"), "CLP" => Some("CLP"), "COP" => Some("COP"),
        "PEN" => Some("PEN"), "UYU" => Some("UYU"), "PYG" => Some("PYG"),
        "BOB" => Some("BOB"), "VES" => Some("VES"), "CRC" => Some("CRC"),
        "GTQ" => Some("GTQ"), "HNL" => Some("HNL"), "NIO" => Some("NIO"),
        "PAB" => Some("PAB"), "DOP" => Some("DOP"), "JMD" => Some("JMD"),
        "TTD" => Some("TTD"), "HTG" => Some("HTG"), "BBD" => Some("BBD"),
        "BSD" => Some("BSD"), "BZD" => Some("BZD"), "GYD" => Some("GYD"),
        "SRD" => Some("SRD"), "AWG" => Some("AWG"), "ANG" => Some("ANG"),
        "BMD" => Some("BMD"), "KYD" => Some("KYD"), "CUP" => Some("CUP"),
        "CUC" => Some("CUC"), "XCD" => Some("XCD"), "ZAR" => Some("ZAR"),
        "EGP" => Some("EGP"), "NGN" => Some("NGN"), "KES" => Some("KES"),
        "GHS" => Some("GHS"), "TZS" => Some("TZS"), "UGX" => Some("UGX"),
        "ETB" => Some("ETB"), "MAD" => Some("MAD"), "TND" => Some("TND"),
        "DZD" => Some("DZD"), "LYD" => Some("LYD"), "XOF" => Some("XOF"),
        "XAF" => Some("XAF"), "CDF" => Some("CDF"), "AOA" => Some("AOA"),
        "MZN" => Some("MZN"), "ZMW" => Some("ZMW"), "BWP" => Some("BWP"),
        "MWK" => Some("MWK"), "RWF" => Some("RWF"), "SOS" => Some("SOS"),
        "SDG" => Some("SDG"), "SCR" => Some("SCR"), "MUR" => Some("MUR"),
        "GMD" => Some("GMD"), "SLL" => Some("SLL"), "GNF" => Some("GNF"),
        "CVE" => Some("CVE"), "NAD" => Some("NAD"), "SZL" => Some("SZL"),
        "LSL" => Some("LSL"), "BIF" => Some("BIF"), "DJF" => Some("DJF"),
        "ERN" => Some("ERN"), "STN" => Some("STN"), "KMF" => Some("KMF"),
        "MGA" => Some("MGA"), "FJD" => Some("FJD"), "PGK" => Some("PGK"),
        "WST" => Some("WST"), "TOP" => Some("TOP"), "VUV" => Some("VUV"),
        "SBD" => Some("SBD"), "KZT" => Some("KZT"), "UZS" => Some("UZS"),
        "KGS" => Some("KGS"), "TJS" => Some("TJS"), "TMT" => Some("TMT"),
        "AFN" => Some("AFN"), "BTN" => Some("BTN"), "XDR" => Some("XDR"),
        "XAU" => Some("XAU"), "XAG" => Some("XAG"),
        _ => None,
    }
}

// ---------------------------------------------------------------------------
// Crypto symbol validation (re-exported from crypto module)
// ---------------------------------------------------------------------------

/// Check if a symbol is a known cryptocurrency (case-insensitive).
pub fn is_crypto_symbol(symbol: &str) -> bool {
    crypto::is_known_crypto(symbol)
}

// ---------------------------------------------------------------------------
// Unified resolution
// ---------------------------------------------------------------------------

/// Resolve any currency reference -- symbol ($, EUR), ISO code, natural-language
/// alias, or crypto symbol -- to a canonical uppercase code.
///
/// Resolution order:
/// 1. Currency symbol (e.g. "$" -> "USD")
/// 2. Exact fiat ISO code (e.g. "EUR" -> "EUR", case-insensitive)
/// 3. Crypto symbol (e.g. "BTC" -> "BTC", case-insensitive)
/// 4. Natural-language alias (e.g. "dollars" -> "USD")
///
/// Returns None if the input is not recognized.
pub fn resolve_currency(input: &str) -> Option<&'static str> {
    // 1. Try symbol first (handles "$", "EUR", "R$", etc.)
    if let Some(code) = resolve_symbol(input) {
        return Some(code);
    }

    // 2. Try exact fiat ISO code (case-insensitive)
    let upper = input.to_uppercase();
    if let Some(code) = resolve_code_static(&upper) {
        return Some(code);
    }

    // 3. Try crypto symbol
    if is_crypto_symbol(&upper) {
        // Return a static str for the matched crypto
        return resolve_crypto_static(&upper);
    }

    // 4. Try natural-language alias
    resolve_alias(input)
}

/// Return a static str for known crypto symbols.
fn resolve_crypto_static(symbol: &str) -> Option<&'static str> {
    crypto::CRYPTO_SYMBOLS
        .iter()
        .find(|(s, _)| *s == symbol)
        .map(|(s, _)| *s)
}

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

    // --- Symbol resolution ---

    #[test]
    fn test_resolve_symbol_usd() {
        assert_eq!(resolve_symbol("$"), Some("USD"));
    }

    #[test]
    fn test_resolve_symbol_eur() {
        assert_eq!(resolve_symbol("€"), Some("EUR"));
    }

    #[test]
    fn test_resolve_symbol_gbp() {
        assert_eq!(resolve_symbol("£"), Some("GBP"));
    }

    #[test]
    fn test_resolve_symbol_jpy() {
        assert_eq!(resolve_symbol("¥"), Some("JPY"));
    }

    #[test]
    fn test_resolve_symbol_brl() {
        assert_eq!(resolve_symbol("R$"), Some("BRL"));
    }

    #[test]
    fn test_resolve_symbol_inr() {
        assert_eq!(resolve_symbol("₹"), Some("INR"));
    }

    #[test]
    fn test_resolve_symbol_krw() {
        assert_eq!(resolve_symbol("₩"), Some("KRW"));
    }

    #[test]
    fn test_resolve_symbol_prefixed_dollar() {
        assert_eq!(resolve_symbol("C$"), Some("CAD"));
        assert_eq!(resolve_symbol("A$"), Some("AUD"));
        assert_eq!(resolve_symbol("NZ$"), Some("NZD"));
        assert_eq!(resolve_symbol("HK$"), Some("HKD"));
        assert_eq!(resolve_symbol("S$"), Some("SGD"));
    }

    #[test]
    fn test_resolve_symbol_unknown() {
        assert_eq!(resolve_symbol("X"), None);
        assert_eq!(resolve_symbol(""), None);
    }

    // --- Alias resolution ---

    #[test]
    fn test_resolve_alias_dollars() {
        assert_eq!(resolve_alias("dollars"), Some("USD"));
        assert_eq!(resolve_alias("dollar"), Some("USD"));
        assert_eq!(resolve_alias("Dollars"), Some("USD"));
        assert_eq!(resolve_alias("bucks"), Some("USD"));
    }

    #[test]
    fn test_resolve_alias_euros() {
        assert_eq!(resolve_alias("euros"), Some("EUR"));
        assert_eq!(resolve_alias("euro"), Some("EUR"));
    }

    #[test]
    fn test_resolve_alias_pounds() {
        assert_eq!(resolve_alias("pounds"), Some("GBP"));
        assert_eq!(resolve_alias("pound"), Some("GBP"));
        assert_eq!(resolve_alias("quid"), Some("GBP"));
    }

    #[test]
    fn test_resolve_alias_yen() {
        assert_eq!(resolve_alias("yen"), Some("JPY"));
    }

    #[test]
    fn test_resolve_alias_crypto() {
        assert_eq!(resolve_alias("bitcoin"), Some("BTC"));
        assert_eq!(resolve_alias("ether"), Some("ETH"));
        assert_eq!(resolve_alias("ethereum"), Some("ETH"));
    }

    #[test]
    fn test_resolve_alias_unknown() {
        assert_eq!(resolve_alias("foo"), None);
    }

    // --- ISO code validation ---

    #[test]
    fn test_is_currency_code_major() {
        assert!(is_currency_code("USD"));
        assert!(is_currency_code("EUR"));
        assert!(is_currency_code("GBP"));
        assert!(is_currency_code("JPY"));
        assert!(is_currency_code("CHF"));
    }

    #[test]
    fn test_is_currency_code_regional() {
        assert!(is_currency_code("BRL"));
        assert!(is_currency_code("MXN"));
        assert!(is_currency_code("ZAR"));
        assert!(is_currency_code("NGN"));
        assert!(is_currency_code("KES"));
    }

    #[test]
    fn test_is_currency_code_negative() {
        assert!(!is_currency_code("usd")); // lowercase
        assert!(!is_currency_code("XYZ"));
        assert!(!is_currency_code("kg"));
        assert!(!is_currency_code(""));
    }

    // --- Unified resolution ---

    #[test]
    fn test_resolve_currency_from_symbol() {
        assert_eq!(resolve_currency("$"), Some("USD"));
        assert_eq!(resolve_currency("€"), Some("EUR"));
        assert_eq!(resolve_currency("R$"), Some("BRL"));
        assert_eq!(resolve_currency("₹"), Some("INR"));
    }

    #[test]
    fn test_resolve_currency_from_code() {
        assert_eq!(resolve_currency("USD"), Some("USD"));
        assert_eq!(resolve_currency("EUR"), Some("EUR"));
        assert_eq!(resolve_currency("usd"), Some("USD"));
        assert_eq!(resolve_currency("eur"), Some("EUR"));
    }

    #[test]
    fn test_resolve_currency_from_crypto() {
        assert_eq!(resolve_currency("BTC"), Some("BTC"));
        assert_eq!(resolve_currency("ETH"), Some("ETH"));
        assert_eq!(resolve_currency("btc"), Some("BTC"));
        assert_eq!(resolve_currency("sol"), Some("SOL"));
    }

    #[test]
    fn test_resolve_currency_from_alias() {
        assert_eq!(resolve_currency("dollars"), Some("USD"));
        assert_eq!(resolve_currency("euros"), Some("EUR"));
        assert_eq!(resolve_currency("pounds"), Some("GBP"));
        assert_eq!(resolve_currency("yen"), Some("JPY"));
        assert_eq!(resolve_currency("bitcoin"), Some("BTC"));
    }

    #[test]
    fn test_resolve_currency_unknown() {
        assert_eq!(resolve_currency("foobar"), None);
        assert_eq!(resolve_currency("kg"), None);
        assert_eq!(resolve_currency("meters"), None);
    }

    // --- Crypto symbol ---

    #[test]
    fn test_is_crypto_symbol_positive() {
        assert!(is_crypto_symbol("BTC"));
        assert!(is_crypto_symbol("ETH"));
        assert!(is_crypto_symbol("btc"));
    }

    #[test]
    fn test_is_crypto_symbol_negative() {
        assert!(!is_crypto_symbol("USD"));
        assert!(!is_crypto_symbol("FOOBAR"));
    }
}
