"""ISO 4217 currency reference data."""

from __future__ import annotations

from dataclasses import dataclass

from finschema.errors import InvalidCurrencyError


@dataclass(frozen=True, slots=True)
class CurrencyInfo:
    code: str
    numeric_code: str
    name: str
    decimals: int
    deprecated: bool = False
    successor: str | None = None


_ACTIVE_CURRENCIES: dict[str, CurrencyInfo] = {
    "AED": CurrencyInfo("AED", "784", "UAE Dirham", 2),
    "AFN": CurrencyInfo("AFN", "971", "Afghani", 2),
    "ALL": CurrencyInfo("ALL", "008", "Lek", 2),
    "AMD": CurrencyInfo("AMD", "051", "Armenian Dram", 2),
    "ANG": CurrencyInfo("ANG", "532", "Netherlands Antillean Guilder", 2),
    "AOA": CurrencyInfo("AOA", "973", "Kwanza", 2),
    "ARS": CurrencyInfo("ARS", "032", "Argentine Peso", 2),
    "AUD": CurrencyInfo("AUD", "036", "Australian Dollar", 2),
    "AWG": CurrencyInfo("AWG", "533", "Aruban Florin", 2),
    "AZN": CurrencyInfo("AZN", "944", "Azerbaijan Manat", 2),
    "BAM": CurrencyInfo("BAM", "977", "Convertible Mark", 2),
    "BBD": CurrencyInfo("BBD", "052", "Barbados Dollar", 2),
    "BDT": CurrencyInfo("BDT", "050", "Taka", 2),
    "BGN": CurrencyInfo("BGN", "975", "Bulgarian Lev", 2),
    "BHD": CurrencyInfo("BHD", "048", "Bahraini Dinar", 3),
    "BIF": CurrencyInfo("BIF", "108", "Burundi Franc", 0),
    "BMD": CurrencyInfo("BMD", "060", "Bermudian Dollar", 2),
    "BND": CurrencyInfo("BND", "096", "Brunei Dollar", 2),
    "BOB": CurrencyInfo("BOB", "068", "Boliviano", 2),
    "BOV": CurrencyInfo("BOV", "984", "Mvdol", 2),
    "BRL": CurrencyInfo("BRL", "986", "Brazilian Real", 2),
    "BSD": CurrencyInfo("BSD", "044", "Bahamian Dollar", 2),
    "BTN": CurrencyInfo("BTN", "064", "Ngultrum", 2),
    "BWP": CurrencyInfo("BWP", "072", "Pula", 2),
    "BYN": CurrencyInfo("BYN", "933", "Belarusian Ruble", 2),
    "BZD": CurrencyInfo("BZD", "084", "Belize Dollar", 2),
    "CAD": CurrencyInfo("CAD", "124", "Canadian Dollar", 2),
    "CDF": CurrencyInfo("CDF", "976", "Congolese Franc", 2),
    "CHE": CurrencyInfo("CHE", "947", "WIR Euro", 2),
    "CHF": CurrencyInfo("CHF", "756", "Swiss Franc", 2),
    "CHW": CurrencyInfo("CHW", "948", "WIR Franc", 2),
    "CLF": CurrencyInfo("CLF", "990", "Unidad de Fomento", 4),
    "CLP": CurrencyInfo("CLP", "152", "Chilean Peso", 0),
    "CNY": CurrencyInfo("CNY", "156", "Yuan Renminbi", 2),
    "COP": CurrencyInfo("COP", "170", "Colombian Peso", 2),
    "COU": CurrencyInfo("COU", "970", "Unidad de Valor Real", 2),
    "CRC": CurrencyInfo("CRC", "188", "Costa Rican Colon", 2),
    "CUC": CurrencyInfo("CUC", "931", "Peso Convertible", 2),
    "CUP": CurrencyInfo("CUP", "192", "Cuban Peso", 2),
    "CVE": CurrencyInfo("CVE", "132", "Cabo Verde Escudo", 2),
    "CZK": CurrencyInfo("CZK", "203", "Czech Koruna", 2),
    "DJF": CurrencyInfo("DJF", "262", "Djibouti Franc", 0),
    "DKK": CurrencyInfo("DKK", "208", "Danish Krone", 2),
    "DOP": CurrencyInfo("DOP", "214", "Dominican Peso", 2),
    "DZD": CurrencyInfo("DZD", "012", "Algerian Dinar", 2),
    "EGP": CurrencyInfo("EGP", "818", "Egyptian Pound", 2),
    "ERN": CurrencyInfo("ERN", "232", "Nakfa", 2),
    "ETB": CurrencyInfo("ETB", "230", "Ethiopian Birr", 2),
    "EUR": CurrencyInfo("EUR", "978", "Euro", 2),
    "FJD": CurrencyInfo("FJD", "242", "Fiji Dollar", 2),
    "FKP": CurrencyInfo("FKP", "238", "Falkland Islands Pound", 2),
    "GBP": CurrencyInfo("GBP", "826", "Pound Sterling", 2),
    "GEL": CurrencyInfo("GEL", "981", "Lari", 2),
    "GHS": CurrencyInfo("GHS", "936", "Ghana Cedi", 2),
    "GIP": CurrencyInfo("GIP", "292", "Gibraltar Pound", 2),
    "GMD": CurrencyInfo("GMD", "270", "Dalasi", 2),
    "GNF": CurrencyInfo("GNF", "324", "Guinean Franc", 0),
    "GTQ": CurrencyInfo("GTQ", "320", "Quetzal", 2),
    "GYD": CurrencyInfo("GYD", "328", "Guyana Dollar", 2),
    "HKD": CurrencyInfo("HKD", "344", "Hong Kong Dollar", 2),
    "HNL": CurrencyInfo("HNL", "340", "Lempira", 2),
    "HRK": CurrencyInfo("HRK", "191", "Kuna", 2),
    "HTG": CurrencyInfo("HTG", "332", "Gourde", 2),
    "HUF": CurrencyInfo("HUF", "348", "Forint", 2),
    "IDR": CurrencyInfo("IDR", "360", "Rupiah", 2),
    "ILS": CurrencyInfo("ILS", "376", "New Israeli Sheqel", 2),
    "INR": CurrencyInfo("INR", "356", "Indian Rupee", 2),
    "IQD": CurrencyInfo("IQD", "368", "Iraqi Dinar", 3),
    "IRR": CurrencyInfo("IRR", "364", "Iranian Rial", 2),
    "ISK": CurrencyInfo("ISK", "352", "Iceland Krona", 0),
    "JMD": CurrencyInfo("JMD", "388", "Jamaican Dollar", 2),
    "JOD": CurrencyInfo("JOD", "400", "Jordanian Dinar", 3),
    "JPY": CurrencyInfo("JPY", "392", "Yen", 0),
    "KES": CurrencyInfo("KES", "404", "Kenyan Shilling", 2),
    "KGS": CurrencyInfo("KGS", "417", "Som", 2),
    "KHR": CurrencyInfo("KHR", "116", "Riel", 2),
    "KMF": CurrencyInfo("KMF", "174", "Comorian Franc", 0),
    "KPW": CurrencyInfo("KPW", "408", "North Korean Won", 2),
    "KRW": CurrencyInfo("KRW", "410", "Won", 0),
    "KWD": CurrencyInfo("KWD", "414", "Kuwaiti Dinar", 3),
    "KYD": CurrencyInfo("KYD", "136", "Cayman Islands Dollar", 2),
    "KZT": CurrencyInfo("KZT", "398", "Tenge", 2),
    "LAK": CurrencyInfo("LAK", "418", "Lao Kip", 2),
    "LBP": CurrencyInfo("LBP", "422", "Lebanese Pound", 2),
    "LKR": CurrencyInfo("LKR", "144", "Sri Lanka Rupee", 2),
    "LRD": CurrencyInfo("LRD", "430", "Liberian Dollar", 2),
    "LSL": CurrencyInfo("LSL", "426", "Loti", 2),
    "LYD": CurrencyInfo("LYD", "434", "Libyan Dinar", 3),
    "MAD": CurrencyInfo("MAD", "504", "Moroccan Dirham", 2),
    "MDL": CurrencyInfo("MDL", "498", "Moldovan Leu", 2),
    "MGA": CurrencyInfo("MGA", "969", "Malagasy Ariary", 2),
    "MKD": CurrencyInfo("MKD", "807", "Denar", 2),
    "MMK": CurrencyInfo("MMK", "104", "Kyat", 2),
    "MNT": CurrencyInfo("MNT", "496", "Tugrik", 2),
    "MOP": CurrencyInfo("MOP", "446", "Pataca", 2),
    "MRU": CurrencyInfo("MRU", "929", "Ouguiya", 2),
    "MUR": CurrencyInfo("MUR", "480", "Mauritius Rupee", 2),
    "MVR": CurrencyInfo("MVR", "462", "Rufiyaa", 2),
    "MWK": CurrencyInfo("MWK", "454", "Malawi Kwacha", 2),
    "MXN": CurrencyInfo("MXN", "484", "Mexican Peso", 2),
    "MXV": CurrencyInfo("MXV", "979", "Mexican Unidad de Inversion (UDI)", 2),
    "MYR": CurrencyInfo("MYR", "458", "Malaysian Ringgit", 2),
    "MZN": CurrencyInfo("MZN", "943", "Mozambique Metical", 2),
    "NAD": CurrencyInfo("NAD", "516", "Namibia Dollar", 2),
    "NGN": CurrencyInfo("NGN", "566", "Naira", 2),
    "NIO": CurrencyInfo("NIO", "558", "Cordoba Oro", 2),
    "NOK": CurrencyInfo("NOK", "578", "Norwegian Krone", 2),
    "NPR": CurrencyInfo("NPR", "524", "Nepalese Rupee", 2),
    "NZD": CurrencyInfo("NZD", "554", "New Zealand Dollar", 2),
    "OMR": CurrencyInfo("OMR", "512", "Rial Omani", 3),
    "PAB": CurrencyInfo("PAB", "590", "Balboa", 2),
    "PEN": CurrencyInfo("PEN", "604", "Sol", 2),
    "PGK": CurrencyInfo("PGK", "598", "Kina", 2),
    "PHP": CurrencyInfo("PHP", "608", "Philippine Peso", 2),
    "PKR": CurrencyInfo("PKR", "586", "Pakistan Rupee", 2),
    "PLN": CurrencyInfo("PLN", "985", "Zloty", 2),
    "PYG": CurrencyInfo("PYG", "600", "Guarani", 0),
    "QAR": CurrencyInfo("QAR", "634", "Qatari Rial", 2),
    "RON": CurrencyInfo("RON", "946", "Romanian Leu", 2),
    "RSD": CurrencyInfo("RSD", "941", "Serbian Dinar", 2),
    "RUB": CurrencyInfo("RUB", "643", "Russian Ruble", 2),
    "RWF": CurrencyInfo("RWF", "646", "Rwanda Franc", 0),
    "SAR": CurrencyInfo("SAR", "682", "Saudi Riyal", 2),
    "SBD": CurrencyInfo("SBD", "090", "Solomon Islands Dollar", 2),
    "SCR": CurrencyInfo("SCR", "690", "Seychelles Rupee", 2),
    "SDG": CurrencyInfo("SDG", "938", "Sudanese Pound", 2),
    "SEK": CurrencyInfo("SEK", "752", "Swedish Krona", 2),
    "SGD": CurrencyInfo("SGD", "702", "Singapore Dollar", 2),
    "SHP": CurrencyInfo("SHP", "654", "Saint Helena Pound", 2),
    "SLE": CurrencyInfo("SLE", "925", "Leone", 2),
    "SLL": CurrencyInfo("SLL", "694", "Leone", 2),
    "SOS": CurrencyInfo("SOS", "706", "Somali Shilling", 2),
    "SRD": CurrencyInfo("SRD", "968", "Surinam Dollar", 2),
    "SSP": CurrencyInfo("SSP", "728", "South Sudanese Pound", 2),
    "STN": CurrencyInfo("STN", "930", "Dobra", 2),
    "SVC": CurrencyInfo("SVC", "222", "El Salvador Colon", 2),
    "SYP": CurrencyInfo("SYP", "760", "Syrian Pound", 2),
    "SZL": CurrencyInfo("SZL", "748", "Lilangeni", 2),
    "THB": CurrencyInfo("THB", "764", "Baht", 2),
    "TJS": CurrencyInfo("TJS", "972", "Somoni", 2),
    "TMT": CurrencyInfo("TMT", "934", "Turkmenistan New Manat", 2),
    "TND": CurrencyInfo("TND", "788", "Tunisian Dinar", 3),
    "TOP": CurrencyInfo("TOP", "776", "Pa’anga", 2),
    "TRY": CurrencyInfo("TRY", "949", "Turkish Lira", 2),
    "TTD": CurrencyInfo("TTD", "780", "Trinidad and Tobago Dollar", 2),
    "TWD": CurrencyInfo("TWD", "901", "New Taiwan Dollar", 2),
    "TZS": CurrencyInfo("TZS", "834", "Tanzanian Shilling", 2),
    "UAH": CurrencyInfo("UAH", "980", "Hryvnia", 2),
    "UGX": CurrencyInfo("UGX", "800", "Uganda Shilling", 0),
    "USD": CurrencyInfo("USD", "840", "US Dollar", 2),
    "USN": CurrencyInfo("USN", "997", "US Dollar (Next day)", 2),
    "UYI": CurrencyInfo("UYI", "940", "Uruguay Peso en Unidades Indexadas (UI)", 0),
    "UYU": CurrencyInfo("UYU", "858", "Peso Uruguayo", 2),
    "UYW": CurrencyInfo("UYW", "927", "Unidad Previsional", 4),
    "UZS": CurrencyInfo("UZS", "860", "Uzbekistan Sum", 2),
    "VED": CurrencyInfo("VED", "926", "Bolívar Soberano", 2),
    "VES": CurrencyInfo("VES", "928", "Bolívar Soberano", 2),
    "VND": CurrencyInfo("VND", "704", "Dong", 0),
    "VUV": CurrencyInfo("VUV", "548", "Vatu", 0),
    "WST": CurrencyInfo("WST", "882", "Tala", 2),
    "XAF": CurrencyInfo("XAF", "950", "CFA Franc BEAC", 0),
    "XAG": CurrencyInfo("XAG", "961", "Silver", 2),
    "XAU": CurrencyInfo("XAU", "959", "Gold", 2),
    "XBA": CurrencyInfo("XBA", "955", "Bond Markets Unit European Composite Unit (EURCO)", 2),
    "XBB": CurrencyInfo("XBB", "956", "Bond Markets Unit European Monetary Unit (E.M.U.-6)", 2),
    "XBC": CurrencyInfo("XBC", "957", "Bond Markets Unit European Unit of Account 9 (E.U.A.-9)", 2),
    "XBD": CurrencyInfo(
        "XBD", "958", "Bond Markets Unit European Unit of Account 17 (E.U.A.-17)", 2
    ),
    "XCD": CurrencyInfo("XCD", "951", "East Caribbean Dollar", 2),
    "XDR": CurrencyInfo("XDR", "960", "SDR (Special Drawing Right)", 2),
    "XOF": CurrencyInfo("XOF", "952", "CFA Franc BCEAO", 0),
    "XPD": CurrencyInfo("XPD", "964", "Palladium", 2),
    "XPF": CurrencyInfo("XPF", "953", "CFP Franc", 0),
    "XPT": CurrencyInfo("XPT", "962", "Platinum", 2),
    "XSU": CurrencyInfo("XSU", "994", "Sucre", 2),
    "XUA": CurrencyInfo("XUA", "965", "ADB Unit of Account", 2),
    "YER": CurrencyInfo("YER", "886", "Yemeni Rial", 2),
    "ZAR": CurrencyInfo("ZAR", "710", "Rand", 2),
    "ZMW": CurrencyInfo("ZMW", "967", "Zambian Kwacha", 2),
    "ZWL": CurrencyInfo("ZWL", "932", "Zimbabwe Dollar", 2),
}

_HISTORICAL_CURRENCIES: dict[str, CurrencyInfo] = {
    "ATS": CurrencyInfo("ATS", "040", "Austrian Schilling", 2, deprecated=True, successor="EUR"),
    "DEM": CurrencyInfo("DEM", "276", "Deutsche Mark", 2, deprecated=True, successor="EUR"),
    "ESP": CurrencyInfo("ESP", "724", "Spanish Peseta", 0, deprecated=True, successor="EUR"),
    "FRF": CurrencyInfo("FRF", "250", "French Franc", 2, deprecated=True, successor="EUR"),
    "ITL": CurrencyInfo("ITL", "380", "Italian Lira", 0, deprecated=True, successor="EUR"),
    "NLG": CurrencyInfo("NLG", "528", "Netherlands Guilder", 2, deprecated=True, successor="EUR"),
    "PTE": CurrencyInfo("PTE", "620", "Portuguese Escudo", 0, deprecated=True, successor="EUR"),
}

CURRENCIES: dict[str, CurrencyInfo] = {**_ACTIVE_CURRENCIES}
HISTORICAL_CURRENCIES: dict[str, CurrencyInfo] = {**_HISTORICAL_CURRENCIES}
CURRENCY_DECIMALS: dict[str, int] = {code: info.decimals for code, info in CURRENCIES.items()}


def get_currency_info(code: str, *, include_historical: bool = False) -> CurrencyInfo:
    normalized = code.upper()
    info = CURRENCIES.get(normalized)
    if info is None and include_historical:
        info = HISTORICAL_CURRENCIES.get(normalized)
    if info is None:
        raise InvalidCurrencyError(
            f"{normalized!r} is not a valid ISO 4217 currency code",
            field="currency",
            expected="active ISO 4217 code",
            actual=normalized,
            details={"currency": normalized},
        )
    return info


def get_currency_decimals(code: str, *, include_historical: bool = False) -> int:
    return get_currency_info(code, include_historical=include_historical).decimals
