lib/money/input/components/flags.ex

defmodule Money.Input.Components.Flags do
  @moduledoc false

  # Currency-code → Unicode regional-indicator flag glyph.
  #
  # Built from a static currency → primary territory table. The
  # flag itself is generated by pairing the territory's two ASCII
  # letters with the Unicode regional-indicator-symbol base
  # (U+1F1E6), which renderers compose into the flag emoji.
  #
  # For currencies whose canonical territory is supranational
  # (EUR → European Union → 🇪🇺) or non-geographic (XAU gold,
  # XDR), we fall back to a neutral white-flag glyph so the row
  # still renders.

  @currency_to_territory %{
    USD: "US",
    CAD: "CA",
    EUR: "EU",
    GBP: "GB",
    JPY: "JP",
    CHF: "CH",
    AUD: "AU",
    NZD: "NZ",
    CNY: "CN",
    HKD: "HK",
    SGD: "SG",
    KRW: "KR",
    INR: "IN",
    BRL: "BR",
    MXN: "MX",
    ZAR: "ZA",
    SEK: "SE",
    NOK: "NO",
    DKK: "DK",
    PLN: "PL",
    CZK: "CZ",
    HUF: "HU",
    RON: "RO",
    TRY: "TR",
    RUB: "RU",
    ILS: "IL",
    AED: "AE",
    SAR: "SA",
    EGP: "EG",
    THB: "TH",
    IDR: "ID",
    PHP: "PH",
    MYR: "MY",
    VND: "VN",
    TWD: "TW",
    UAH: "UA",
    ARS: "AR",
    COP: "CO",
    PEN: "PE",
    CLP: "CL",
    PKR: "PK",
    BDT: "BD",
    NGN: "NG",
    KES: "KE",
    KZT: "KZ",
    MAD: "MA",
    BHD: "BH",
    KWD: "KW",
    QAR: "QA",
    OMR: "OM",
    JOD: "JO",
    LKR: "LK",
    NPR: "NP",
    ISK: "IS",
    BGN: "BG",
    HRK: "HR",
    RSD: "RS",
    UYU: "UY",
    DOP: "DO",
    GTQ: "GT",
    PAB: "PA",
    HNL: "HN",
    NIO: "NI",
    JMD: "JM",
    BBD: "BB",
    BSD: "BS",
    XCD: "AG",
    BMD: "BM",
    KYD: "KY",
    TTD: "TT",
    XOF: "SN",
    XAF: "CM",
    XPF: "PF",
    GHS: "GH",
    UGX: "UG",
    TZS: "TZ",
    RWF: "RW",
    ETB: "ET",
    DZD: "DZ",
    TND: "TN",
    LYD: "LY",
    LBP: "LB",
    SYP: "SY",
    IQD: "IQ",
    IRR: "IR",
    AFN: "AF",
    YER: "YE",
    AMD: "AM",
    AZN: "AZ",
    BYN: "BY",
    GEL: "GE",
    MDL: "MD",
    KGS: "KG",
    UZS: "UZ",
    TMT: "TM",
    MNT: "MN",
    MMK: "MM",
    LAK: "LA",
    KHR: "KH",
    BND: "BN",
    PGK: "PG",
    FJD: "FJ"
  }

  @country_names %{
    "US" => "United States",
    "CA" => "Canada",
    "EU" => "European Union",
    "GB" => "United Kingdom",
    "JP" => "Japan",
    "CH" => "Switzerland",
    "AU" => "Australia",
    "NZ" => "New Zealand",
    "CN" => "China",
    "HK" => "Hong Kong",
    "SG" => "Singapore",
    "KR" => "South Korea",
    "IN" => "India",
    "BR" => "Brazil",
    "MX" => "Mexico",
    "ZA" => "South Africa",
    "SE" => "Sweden",
    "NO" => "Norway",
    "DK" => "Denmark",
    "PL" => "Poland",
    "CZ" => "Czechia",
    "HU" => "Hungary",
    "RO" => "Romania",
    "TR" => "Türkiye",
    "RU" => "Russia",
    "IL" => "Israel",
    "AE" => "United Arab Emirates",
    "SA" => "Saudi Arabia",
    "EG" => "Egypt",
    "TH" => "Thailand",
    "ID" => "Indonesia",
    "PH" => "Philippines",
    "MY" => "Malaysia",
    "VN" => "Vietnam",
    "TW" => "Taiwan",
    "UA" => "Ukraine",
    "AR" => "Argentina",
    "CO" => "Colombia",
    "PE" => "Peru",
    "CL" => "Chile",
    "PK" => "Pakistan",
    "BD" => "Bangladesh",
    "NG" => "Nigeria",
    "KE" => "Kenya",
    "KZ" => "Kazakhstan",
    "MA" => "Morocco",
    "BH" => "Bahrain",
    "KW" => "Kuwait"
  }

  @doc "Returns a Unicode flag glyph for a currency code, or a neutral fallback."
  @spec flag_for(atom() | String.t()) :: String.t()
  def flag_for(code) when is_atom(code) do
    case Map.get(@currency_to_territory, code) do
      nil -> "🏳"
      "EU" -> "🇪🇺"
      territory -> territory_flag(territory)
    end
  end

  def flag_for(code) when is_binary(code) do
    try do
      flag_for(String.to_existing_atom(code))
    rescue
      ArgumentError -> "🏳"
    end
  end

  @doc "Returns a primary country/territory name for a currency code."
  @spec country_for(atom() | String.t()) :: String.t() | nil
  def country_for(code) when is_atom(code) do
    case Map.get(@currency_to_territory, code) do
      nil -> nil
      territory -> Map.get(@country_names, territory, territory)
    end
  end

  def country_for(_), do: nil

  # Build the flag emoji from a 2-letter ISO 3166-1 alpha-2 code by
  # pairing each ASCII letter with U+1F1E6 (regional indicator A).
  defp territory_flag(<<a::utf8, b::utf8>>) when a in ?A..?Z and b in ?A..?Z do
    <<a - ?A + 0x1F1E6::utf8, b - ?A + 0x1F1E6::utf8>>
  end

  defp territory_flag(_), do: "🏳"
end