/**
 * 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: '$', label: 'US Dollar' },
  { code: 'EUR', symbol: '€', label: 'Euro' },
  { code: 'GBP', symbol: '£', label: 'British Pound' },
  { code: 'CAD', symbol: 'CA$', label: 'Canadian Dollar' },
  { code: 'AUD', symbol: 'A$', label: 'Australian Dollar' },
  { code: 'CHF', symbol: 'CHF', label: 'Swiss Franc' },
  { code: 'JPY', symbol: '¥', label: 'Japanese Yen' },
  { code: 'AED', symbol: 'د.إ', label: 'UAE Dirham' },
  { code: 'SGD', symbol: 'S$', label: 'Singapore Dollar' },
  { code: 'HKD', symbol: 'HK$', label: 'Hong Kong Dollar' },
] as const;

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

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).
 *
 * Defaults to `en-US` locale because the CRM is single-locale today;
 * pass `locale` explicitly when rendering for portal users in the
 * future. 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 = 'en-US', 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();
}
