/* Multi-currency helpers for the pricing flow.

   Pure module — no next/headers, no React, no DB. Safe to import from
   server components, client components, server actions, scripts, and tests.

   Server-only cookie helpers live in ./currency-cookie.ts.

   Source: docs/superpowers/specs/2026-05-25-org-roles-stripe-billing-design.md
   and the multi-currency plan in /Users/humperdoo/.claude/plans/il-faut-setup-le-valiant-kay.md. */

export type Currency = "eur" | "usd" | "gbp" | "jpy" | "cad";

export type CurrencyMeta = {
  code: Currency;
  /* Display symbol used in plain-text contexts (Intl.NumberFormat handles
     the formatted output itself). */
  symbol: string;
  /* Default BCP-47 locale used when formatPrice() is called without an
     explicit locale override. */
  locale: string;
  /* True for currencies that don't have a fractional unit (JPY, KRW, ...).
     Drives both Intl formatting and toMinorUnits() conversion. */
  zeroDecimal: boolean;
};

export const SUPPORTED_CURRENCIES: readonly CurrencyMeta[] = [
  { code: "eur", symbol: "€", locale: "fr-FR", zeroDecimal: false },
  { code: "usd", symbol: "$", locale: "en-US", zeroDecimal: false },
  { code: "gbp", symbol: "£", locale: "en-GB", zeroDecimal: false },
  { code: "jpy", symbol: "¥", locale: "ja-JP", zeroDecimal: true },
  { code: "cad", symbol: "C$", locale: "en-CA", zeroDecimal: false },
] as const;

export const CURRENCY_COOKIE_NAME = "eleva_pricing_currency";

/* ISO-3166 alpha-2 (uppercase) → Currency. EU member states map to EUR.
   Countries without a native plan currency fall back to USD when the
   region is broadly USD-aligned, otherwise to EUR. Used by
   detectCurrencyFromHeaders() with the x-vercel-ip-country header. */
export const COUNTRY_TO_CURRENCY: Record<string, Currency> = {
  /* USD-native */
  US: "usd",
  /* GBP-native */
  GB: "gbp",
  /* JPY-native */
  JP: "jpy",
  /* CAD-native */
  CA: "cad",
  /* Eurozone */
  FR: "eur",
  DE: "eur",
  BE: "eur",
  NL: "eur",
  IT: "eur",
  ES: "eur",
  PT: "eur",
  IE: "eur",
  LU: "eur",
  AT: "eur",
  FI: "eur",
  GR: "eur",
  SK: "eur",
  SI: "eur",
  LV: "eur",
  LT: "eur",
  EE: "eur",
  CY: "eur",
  MT: "eur",
  /* Oceania — no native plan currency, fall back to usd */
  AU: "usd",
  NZ: "usd",
  /* Wider Europe — no native plan currency, fall back to eur */
  CH: "eur",
  NO: "eur",
  SE: "eur",
  DK: "eur",
  PL: "eur",
  CZ: "eur",
  HU: "eur",
  RO: "eur",
  BG: "eur",
};

const CURRENCY_META_BY_CODE: Record<Currency, CurrencyMeta> = SUPPORTED_CURRENCIES.reduce(
  (acc, meta) => {
    acc[meta.code] = meta;
    return acc;
  },
  {} as Record<Currency, CurrencyMeta>,
);

export function isSupportedCurrency(value: unknown): value is Currency {
  return (
    typeof value === "string" &&
    (value === "eur" || value === "usd" || value === "gbp" || value === "jpy" || value === "cad")
  );
}

/* Extract the first parseable region tag from an Accept-Language header.
   Returns the uppercased region or null. */
function regionFromAcceptLanguage(header: string): string | null {
  for (const part of header.split(",")) {
    const tag = part.split(";")[0]?.trim();
    if (!tag) continue;
    /* Tag forms: "en-US", "fr-FR", "en", "zh-Hant-TW", ... */
    const segments = tag.split("-");
    /* Find a 2-letter region segment (positions vary because of script
       subtags). */
    for (const seg of segments.slice(1)) {
      if (seg.length === 2 && /^[A-Za-z]{2}$/.test(seg)) {
        return seg.toUpperCase();
      }
    }
  }
  return null;
}

/* Resolve the user's preferred plan currency from request headers.

   Header precedence (first hit wins):
     1. x-vercel-ip-country (set by Vercel edge for production traffic)
     2. accept-language     (region subtag of the highest-priority locale)
   Falls back to "eur" when neither yields a known mapping. */
export function detectCurrencyFromHeaders(headers: Headers): Currency {
  const country = headers.get("x-vercel-ip-country");
  if (country) {
    const mapped = COUNTRY_TO_CURRENCY[country.toUpperCase()];
    if (mapped) return mapped;
  }

  const acceptLanguage = headers.get("accept-language");
  if (acceptLanguage) {
    const region = regionFromAcceptLanguage(acceptLanguage);
    if (region) {
      const mapped = COUNTRY_TO_CURRENCY[region];
      if (mapped) return mapped;
    }
  }

  return "eur";
}

/* Format an amount expressed in MAJOR units (e.g. 19 → "€19.00",
   2900 → "¥2,900") using Intl.NumberFormat. The currency's zeroDecimal
   flag drives the fraction digits — JPY rounds to integer, others to 2dp.
   Pass `locale` to override the currency's default locale (useful when the
   UI locale differs from the currency's home locale). */
export function formatPrice(amount: number, currency: Currency, locale?: string): string {
  const meta = CURRENCY_META_BY_CODE[currency];
  const fractionDigits = meta.zeroDecimal ? 0 : 2;
  return new Intl.NumberFormat(locale ?? meta.locale, {
    style: "currency",
    currency: currency.toUpperCase(),
    minimumFractionDigits: fractionDigits,
    maximumFractionDigits: fractionDigits,
  }).format(amount);
}
