/**
 * Working out someone's country, language and currency from their request.
 *
 * Deliberately no third-party lookup: the browser already tells us its
 * preferred language, and hosts commonly add a country header. That covers
 * most people without another dependency or another thing to pay for.
 */

/** Currency per country, for the ones we offer. */
const COUNTRY_CURRENCY: Record<string, string> = {
  GB: "GBP", IE: "EUR", US: "USD", CA: "CAD", AU: "AUD", NZ: "AUD",
  TR: "TRY", DE: "EUR", FR: "EUR", ES: "EUR", IT: "EUR", NL: "EUR",
  BE: "EUR", PT: "EUR", AT: "EUR", GR: "EUR", FI: "EUR",
  JP: "JPY", IN: "INR", BR: "USD", NG: "USD", ZA: "USD",
};

/** Language per country, where the browser doesn't say. */
const COUNTRY_LANGUAGE: Record<string, string> = {
  GB: "English", US: "English", CA: "English", AU: "English", IE: "English",
  TR: "Türkçe", DE: "Deutsch", AT: "Deutsch",
  FR: "Français", BE: "Français",
  ES: "Español", MX: "Español", AR: "Español",
  PT: "Português", BR: "Português",
  SA: "العربية", AE: "العربية", EG: "العربية",
};

const LANGUAGE_BY_TAG: Record<string, string> = {
  en: "English", tr: "Türkçe", es: "Español", fr: "Français",
  de: "Deutsch", pt: "Português", ar: "العربية",
};

/** When there's no country header, the language is the next best guess. */
const LANGUAGE_CURRENCY: Record<string, string> = {
  tr: "TRY", de: "EUR", fr: "EUR", es: "EUR", it: "EUR", nl: "EUR",
  pt: "EUR", "en-gb": "GBP", ja: "JPY", hi: "INR",
};

const COUNTRY_NAMES: Record<string, string> = {
  GB: "United Kingdom", US: "United States", TR: "Turkey", DE: "Germany",
  FR: "France", ES: "Spain", NL: "Netherlands", CA: "Canada",
  AU: "Australia", IN: "India", BR: "Brazil", NG: "Nigeria",
  IE: "Ireland", IT: "Italy", PT: "Portugal", ZA: "South Africa",
};

export type Locale = {
  country: string;
  /** The reader's own currency, for showing an approximate price. */
  localCurrency?: string;
  /** The site's timezone. */
  timezone?: string;
  /** Whether the reader's own zone should be used instead. */
  detectTimezone?: boolean;
  countryCode: string;
  language: string;
  currency: string;
  /** So the UI can say whether this was detected or is just the fallback. */
  detected: boolean;
};

/**
 * Reads what we can from the request headers, falling back to the admin's
 * defaults for anything we can't tell.
 */
export function detectLocale(
  headers: Headers,
  settings: Record<string, unknown>
): Locale {
  const fallback: Locale = {
    country: String(settings.defaultCountry ?? "United Kingdom"),
    countryCode: "",
    language: String(settings.defaultLanguage ?? "English"),
    currency: String(settings.currency ?? "USD"),
    detected: false,
  };

  // Most hosts add one of these; none is guaranteed.
  const code = (
    headers.get("cf-ipcountry") ??
    headers.get("x-vercel-ip-country") ??
    headers.get("x-country-code") ??
    ""
  ).toUpperCase();

  const wantsCountry = settings.autoDetectLocale !== false;
  // The site's currency is always the one shown against a price. Swapping
  // just the symbol made 100 dollars read as 100 lira, which is why that
  // option is gone; an approximate local figure is offered alongside
  // instead, under "Show prices in the reader's own money".
  const wantsCurrency = false;

  // What the reader's own money is, whatever the site prices in. Reported
  // separately so a converted figure can be offered alongside the real
  // price without either being mistaken for the other.
  const readerCurrency =
    (code && COUNTRY_CURRENCY[code]) ||
    (() => {
      const accept = (headers.get("accept-language") ?? "").toLowerCase();
      const full = accept.split(",")[0]?.trim() ?? "";
      return LANGUAGE_CURRENCY[full] ?? LANGUAGE_CURRENCY[full.split("-")[0]];
    })();
  // When off, everyone sees times in the site's own timezone.
  const wantsTimezone = settings.autoDetectTimezone !== false;

  let out = { ...fallback };

  if (wantsCountry && code && COUNTRY_NAMES[code]) {
    out = {
      ...out,
      country: COUNTRY_NAMES[code],
      countryCode: code,
      language: COUNTRY_LANGUAGE[code] ?? out.language,
      detected: true,
    };
  }

  // The browser's own preference beats a guess from the country.
  if (wantsCountry) {
    const accept = headers.get("accept-language") ?? "";
    const tag = accept.split(",")[0]?.split("-")[0]?.toLowerCase();
    if (tag && LANGUAGE_BY_TAG[tag]) {
      out.language = LANGUAGE_BY_TAG[tag];
      out.detected = true;
    }
  }

  if (wantsCurrency) {
    if (code && COUNTRY_CURRENCY[code]) {
      out.currency = COUNTRY_CURRENCY[code];
      out.detected = true;
    } else {
      // Most hosts don't send a country header at all, so fall back to the
      // language — a Turkish browser almost certainly wants lira.
      const accept = (headers.get("accept-language") ?? "").toLowerCase();
      const full = accept.split(",")[0]?.trim() ?? "";
      const short = full.split("-")[0];
      const guess = LANGUAGE_CURRENCY[full] ?? LANGUAGE_CURRENCY[short];
      if (guess) {
        out.currency = guess;
        out.detected = true;
      }
    }
  }

  // The site's timezone is always sent; detection decides whether the
  // reader's own is used instead.
  out.timezone = String(settings.timezone ?? "Europe/London");
  out.detectTimezone = wantsTimezone;

  if (readerCurrency) out.localCurrency = readerCurrency;

  return out;
}

/** The symbol to show against an amount. */
export function currencySymbol(code: string) {
  return (
    { USD: "$", EUR: "€", GBP: "£", TRY: "₺", CAD: "$", AUD: "$", JPY: "¥", INR: "₹" }[
      code
    ] ?? "$"
  );
}
