/**
 * Single source of truth for money formatting + the supported-currency
 * dropdown list. Everywhere in the CRM that renders an amount should
 * call `formatCurrency()` rather than spinning up its own
 * `Intl.NumberFormat`, and every form that captures an amount should
 * use the curated `SUPPORTED_CURRENCIES` list rather than a free-text
 * 3-letter input.
 */

export const SUPPORTED_CURRENCIES = [
  { code: 'USD', symbol: '$' },
  { code: 'EUR', symbol: '€' },
  { code: 'GBP', symbol: '£' },
  { code: 'CAD', symbol: 'CA$' },
  { code: 'AUD', symbol: 'A$' },
  { code: 'CHF', symbol: 'CHF' },
  { code: 'JPY', symbol: '¥' },
  { code: 'AED', symbol: 'د.إ' },
  { code: 'SGD', symbol: 'S$' },
  { code: 'HKD', symbol: 'HK$' },
  { code: 'PAB', symbol: 'B/.' },
  { code: 'XCD', symbol: 'EC$' },
  { code: 'BSD', symbol: 'B$' },
  { code: 'KYD', symbol: 'CI$' },
  { code: 'BBD', symbol: 'Bds$' },
  { code: 'DOP', symbol: 'RD$' },
  { code: 'JMD', symbol: 'J$' },
  { code: 'TTD', symbol: 'TT$' },
] as const;

export type SupportedCurrencyCode = (typeof SUPPORTED_CURRENCIES)[number]['code'];

/**
 * Localized human-readable label for an ISO currency code (e.g.
 * 'USD' → 'US Dollar' in English, 'dolar estadounidense' in Spanish).
 * Uses `Intl.DisplayNames`, so the result tracks the user's locale
 * automatically. Falls back to the bare code when the runtime can't
 * resolve a name (e.g. unknown code or older Node without DisplayNames).
 */
export function currencyLabel(code: string, locale?: string): string {
  const upper = code.toUpperCase();
  try {
    const display = new Intl.DisplayNames(locale ? [locale] : undefined, {
      type: 'currency',
    });
    return display.of(upper) ?? upper;
  } catch {
    return upper;
  }
}

const SUPPORTED_SET: ReadonlySet<string> = new Set(SUPPORTED_CURRENCIES.map((c) => c.code));

/**
 * Format an amount for display. Accepts numbers, numeric strings, and
 * null/undefined (returns the empty string for the latter so callers
 * can short-circuit display logic).
 *
 * When `options.locale` is omitted the runtime's default locale is used
 * (Intl falls back to the browser/Node default). Pass `locale` explicitly
 * to force a specific rendering. Unknown ISO codes fall through to Intl
 * unchanged so the function never throws on legacy data.
 */
export function formatCurrency(
  amount: number | string | null | undefined,
  currency: string | null | undefined = 'USD',
  options: { locale?: string; minFractionDigits?: number; maxFractionDigits?: number } = {},
): string {
  if (amount === null || amount === undefined || amount === '') return '';
  const value = typeof amount === 'number' ? amount : Number(amount);
  if (!Number.isFinite(value)) return '';
  // Defensive: trim + uppercase + fallback to USD when blank. Legacy
  // NocoDB rows sometimes contain whitespace/empty strings rather than
  // proper ISO codes, which would make Intl.NumberFormat throw.
  const rawCode = (currency ?? 'USD').trim().toUpperCase();
  const code = rawCode === '' ? 'USD' : rawCode;
  // If the caller only specifies max (common: maxFractionDigits: 0 for
  // whole-currency display), clamp min down to match - otherwise
  // `toLocaleString` throws "Computed minimumFractionDigits is larger
  // than maximumFractionDigits". The same defensive clamp protects
  // Intl.NumberFormat too.
  const { locale, maxFractionDigits = 2 } = options;
  const minFractionDigits = options.minFractionDigits ?? Math.min(2, maxFractionDigits);
  try {
    return new Intl.NumberFormat(locale, {
      style: 'currency',
      currency: code,
      minimumFractionDigits: minFractionDigits,
      maximumFractionDigits: maxFractionDigits,
    }).format(value);
  } catch {
    // Unknown currency code - format the number with thousand
    // separators (still useful) and append the code as a suffix rather
    // than letting Intl crash the render. Keeps display robust against
    // any legacy NocoDB rows that smuggled non-ISO strings.
    const formatted = value.toLocaleString(locale, {
      minimumFractionDigits: minFractionDigits,
      maximumFractionDigits: maxFractionDigits,
    });
    return `${formatted} ${code}`;
  }
}

/** Whether a 3-letter code is in the curated supported list. */
export function isSupportedCurrency(code: string): boolean {
  return SUPPORTED_SET.has(code.toUpperCase());
}

/** Symbol for a given currency code, falling back to the code itself. */
export function currencySymbol(code: string): string {
  const match = SUPPORTED_CURRENCIES.find((c) => c.code === code.toUpperCase());
  return match?.symbol ?? code.toUpperCase();
}
