import { describe, expect, it } from "vitest";

import { detectCurrencyFromHeaders, formatPrice, isSupportedCurrency } from "./currency";

describe("isSupportedCurrency", () => {
  it("accepts every supported code", () => {
    expect(isSupportedCurrency("eur")).toBe(true);
    expect(isSupportedCurrency("usd")).toBe(true);
    expect(isSupportedCurrency("gbp")).toBe(true);
    expect(isSupportedCurrency("jpy")).toBe(true);
    expect(isSupportedCurrency("cad")).toBe(true);
  });

  it("rejects unknown / malformed strings", () => {
    expect(isSupportedCurrency("EUR")).toBe(false);
    expect(isSupportedCurrency("chf")).toBe(false);
    expect(isSupportedCurrency("")).toBe(false);
  });

  it("rejects non-string values", () => {
    expect(isSupportedCurrency(undefined)).toBe(false);
    expect(isSupportedCurrency(null)).toBe(false);
    expect(isSupportedCurrency(42)).toBe(false);
    expect(isSupportedCurrency({})).toBe(false);
  });
});

describe("detectCurrencyFromHeaders", () => {
  function headersOf(init: Record<string, string>): Headers {
    return new Headers(init);
  }

  it("maps x-vercel-ip-country: US → usd", () => {
    expect(detectCurrencyFromHeaders(headersOf({ "x-vercel-ip-country": "US" }))).toBe("usd");
  });

  it("maps x-vercel-ip-country: FR → eur", () => {
    expect(detectCurrencyFromHeaders(headersOf({ "x-vercel-ip-country": "FR" }))).toBe("eur");
  });

  it("maps x-vercel-ip-country: JP → jpy", () => {
    expect(detectCurrencyFromHeaders(headersOf({ "x-vercel-ip-country": "JP" }))).toBe("jpy");
  });

  it("maps x-vercel-ip-country: CA → cad", () => {
    expect(detectCurrencyFromHeaders(headersOf({ "x-vercel-ip-country": "CA" }))).toBe("cad");
  });

  it("maps x-vercel-ip-country: GB → gbp", () => {
    expect(detectCurrencyFromHeaders(headersOf({ "x-vercel-ip-country": "GB" }))).toBe("gbp");
  });

  it("falls back to eur for unknown country codes", () => {
    expect(detectCurrencyFromHeaders(headersOf({ "x-vercel-ip-country": "XX" }))).toBe("eur");
  });

  it("falls back to accept-language region when no ip country is set", () => {
    expect(detectCurrencyFromHeaders(headersOf({ "accept-language": "en-US,fr;q=0.9" }))).toBe(
      "usd",
    );
  });

  it("uses accept-language region for fr-FR → eur", () => {
    expect(detectCurrencyFromHeaders(headersOf({ "accept-language": "fr-FR" }))).toBe("eur");
  });

  it("returns eur when no headers are present", () => {
    expect(detectCurrencyFromHeaders(headersOf({}))).toBe("eur");
  });
});

describe("formatPrice", () => {
  it("formats EUR in fr-FR with a decimal comma", () => {
    const formatted = formatPrice(19, "eur", "fr-FR");
    expect(formatted).toMatch(/19,00/);
    expect(formatted).toContain("€");
  });

  it("formats USD in en-US as $19.00", () => {
    expect(formatPrice(19, "usd", "en-US")).toBe("$19.00");
  });

  it("formats JPY without decimals (zero-decimal currency)", () => {
    const formatted = formatPrice(2900, "jpy", "ja-JP");
    expect(formatted).toMatch(/2,900/);
    expect(formatted).not.toMatch(/[.,]\d{2}\b/);
    /* Tolerate NBSP / narrow-NBSP between the symbol and the digits, and
       both the ASCII ¥ (U+00A5) and the fullwidth ￥ (U+FFE5) — ICU's
       ja-JP build uses the fullwidth form. */
    expect(formatted.replace(/\s+/gu, "")).toMatch(/^[¥￥]2,900$/u);
  });

  it("formats GBP with the £ symbol when no locale override is given", () => {
    const formatted = formatPrice(149, "gbp");
    expect(formatted).toContain("£");
    expect(formatted).toContain("149");
  });
});
