/**
 * Unit tests for lib/utils/currency.ts
 * Tests currency detection, conversion, formatting, Stripe price matching
 */

import { CurrencyManager } from "@/lib/utils/currency";
import type { Currency } from "@/lib/types/pricing";

// Mock window for server-side tests
const mockWindow = (timezone?: string) => {
  if (timezone) {
    Object.defineProperty(global, "Intl", {
      writable: true,
      value: {
        DateTimeFormat: jest.fn(() => ({
          resolvedOptions: () => ({ timeZone: timezone }),
        })),
      },
    });
  } else {
    // Remove window
    delete (global as any).window;
  }
};

describe("lib/utils/currency.ts", () => {
  describe("CurrencyManager.detectUserCurrency", () => {
    afterEach(() => {
      jest.restoreAllMocks();
    });

    it("should return GBP on server-side (no window)", () => {
      mockWindow();
      const currency = CurrencyManager.detectUserCurrency();
      expect(currency).toBe("GBP");
    });

    it("should detect SAR for Riyadh timezone", () => {
      (global as any).window = {};
      mockWindow("Asia/Riyadh");
      const currency = CurrencyManager.detectUserCurrency();
      expect(currency).toBe("SAR");
    });

    it("should detect AED for Dubai timezone", () => {
      (global as any).window = {};
      mockWindow("Asia/Dubai");
      const currency = CurrencyManager.detectUserCurrency();
      expect(currency).toBe("AED");
    });

    it("should detect AED for Abu Dhabi timezone", () => {
      (global as any).window = {};
      mockWindow("Asia/Abu_Dhabi");
      const currency = CurrencyManager.detectUserCurrency();
      expect(currency).toBe("AED");
    });

    it("should detect QAR for Qatar timezone", () => {
      (global as any).window = {};
      mockWindow("Asia/Qatar");
      const currency = CurrencyManager.detectUserCurrency();
      expect(currency).toBe("QAR");
    });

    it("should detect QAR for Doha timezone", () => {
      (global as any).window = {};
      mockWindow("Asia/Doha");
      const currency = CurrencyManager.detectUserCurrency();
      expect(currency).toBe("QAR");
    });

    it("should detect KWD for Kuwait timezone", () => {
      (global as any).window = {};
      mockWindow("Asia/Kuwait");
      const currency = CurrencyManager.detectUserCurrency();
      expect(currency).toBe("KWD");
    });

    it("should detect BHD for Bahrain timezone", () => {
      (global as any).window = {};
      mockWindow("Asia/Bahrain");
      const currency = CurrencyManager.detectUserCurrency();
      expect(currency).toBe("BHD");
    });

    it("should detect BHD for Manama timezone", () => {
      (global as any).window = {};
      mockWindow("Asia/Manama");
      const currency = CurrencyManager.detectUserCurrency();
      expect(currency).toBe("BHD");
    });

    it("should detect OMR for Muscat timezone", () => {
      (global as any).window = {};
      mockWindow("Asia/Muscat");
      const currency = CurrencyManager.detectUserCurrency();
      expect(currency).toBe("OMR");
    });

    it("should detect USD for New York timezone", () => {
      (global as any).window = {};
      mockWindow("America/New_York");
      const currency = CurrencyManager.detectUserCurrency();
      expect(currency).toBe("USD");
    });

    it("should detect USD for Los Angeles timezone", () => {
      (global as any).window = {};
      mockWindow("America/Los_Angeles");
      const currency = CurrencyManager.detectUserCurrency();
      expect(currency).toBe("USD");
    });

    it("should detect USD for Chicago timezone", () => {
      (global as any).window = {};
      mockWindow("America/Chicago");
      const currency = CurrencyManager.detectUserCurrency();
      expect(currency).toBe("USD");
    });

    it("should detect GBP for London timezone", () => {
      (global as any).window = {};
      mockWindow("Europe/London");
      const currency = CurrencyManager.detectUserCurrency();
      expect(currency).toBe("GBP");
    });

    it("should detect EUR for Paris timezone", () => {
      (global as any).window = {};
      mockWindow("Europe/Paris");
      const currency = CurrencyManager.detectUserCurrency();
      expect(currency).toBe("EUR");
    });

    it("should detect EUR for Berlin timezone", () => {
      (global as any).window = {};
      mockWindow("Europe/Berlin");
      const currency = CurrencyManager.detectUserCurrency();
      expect(currency).toBe("EUR");
    });

    it("should detect EUR for Madrid timezone", () => {
      (global as any).window = {};
      mockWindow("Europe/Madrid");
      const currency = CurrencyManager.detectUserCurrency();
      expect(currency).toBe("EUR");
    });

    it("should detect EUR for generic Europe timezone (not London)", () => {
      (global as any).window = {};
      mockWindow("Europe/Sofia"); // Not in explicit list
      const currency = CurrencyManager.detectUserCurrency();
      expect(currency).toBe("EUR");
    });

    it("should default to GBP for unknown timezones", () => {
      (global as any).window = {};
      mockWindow("Asia/Tokyo");
      const currency = CurrencyManager.detectUserCurrency();
      expect(currency).toBe("GBP");
    });

    it("should handle timezone detection errors gracefully", () => {
      (global as any).window = {};
      Object.defineProperty(global, "Intl", {
        writable: true,
        value: {
          DateTimeFormat: jest.fn(() => {
            throw new Error("Timezone error");
          }),
        },
      });

      const currency = CurrencyManager.detectUserCurrency();
      expect(currency).toBe("GBP");
    });
  });

  describe("CurrencyManager.convert", () => {
    it("should convert GBP to USD correctly", () => {
      const converted = CurrencyManager.convert(100, "USD");
      expect(converted).toBe(127); // 100 * 1.27 = 127
    });

    it("should convert GBP to SAR correctly", () => {
      const converted = CurrencyManager.convert(100, "SAR");
      expect(converted).toBe(476); // 100 * 4.76 = 476
    });

    it("should convert GBP to AED correctly", () => {
      const converted = CurrencyManager.convert(100, "AED");
      expect(converted).toBe(466); // 100 * 4.66 = 466
    });

    it("should convert GBP to EUR correctly", () => {
      const converted = CurrencyManager.convert(100, "EUR");
      expect(converted).toBe(117); // 100 * 1.17 = 117
    });

    it("should return same amount for GBP to GBP", () => {
      const converted = CurrencyManager.convert(100, "GBP");
      expect(converted).toBe(100); // 100 * 1 = 100
    });

    it("should round to nearest integer", () => {
      const converted = CurrencyManager.convert(99, "USD");
      expect(converted).toBe(126); // 99 * 1.27 = 125.73 → 126
    });

    it("should handle zero amount", () => {
      const converted = CurrencyManager.convert(0, "USD");
      expect(converted).toBe(0);
    });

    it("should handle decimal amounts", () => {
      const converted = CurrencyManager.convert(50.5, "USD");
      expect(converted).toBe(64); // 50.5 * 1.27 = 64.135 → 64
    });
  });

  describe("CurrencyManager.formatPrice", () => {
    it("should format GBP with £ symbol", () => {
      const formatted = CurrencyManager.formatPrice(100, "GBP");
      expect(formatted).toBe("£100");
    });

    it("should format USD with $ symbol", () => {
      const formatted = CurrencyManager.formatPrice(100, "USD");
      expect(formatted).toBe("$100");
    });

    it("should format SAR with Arabic symbol", () => {
      const formatted = CurrencyManager.formatPrice(100, "SAR");
      expect(formatted).toBe("ر.س100");
    });

    it("should format AED with Arabic symbol", () => {
      const formatted = CurrencyManager.formatPrice(100, "AED");
      expect(formatted).toBe("د.إ100");
    });

    it("should include thousand separators", () => {
      const formatted = CurrencyManager.formatPrice(1000, "USD");
      expect(formatted).toContain("1"); // Will have comma separator
    });

    it("should handle zero amount", () => {
      const formatted = CurrencyManager.formatPrice(0, "GBP");
      expect(formatted).toBe("£0");
    });
  });

  describe("CurrencyManager.getCurrencyInfo", () => {
    it("should return correct info for GBP", () => {
      const info = CurrencyManager.getCurrencyInfo("GBP");
      expect(info).toEqual({
        rate: 1,
        symbol: "£",
        code: "GBP",
      });
    });

    it("should return correct info for SAR", () => {
      const info = CurrencyManager.getCurrencyInfo("SAR");
      expect(info).toEqual({
        rate: 4.76,
        symbol: "ر.س",
        code: "SAR",
      });
    });

    it("should return correct info for all GCC currencies", () => {
      const gccCurrencies: Currency[] = [
        "SAR",
        "AED",
        "QAR",
        "KWD",
        "BHD",
        "OMR",
      ];

      for (const currency of gccCurrencies) {
        const info = CurrencyManager.getCurrencyInfo(currency);
        expect(info).toBeDefined();
        expect(info.code).toBe(currency);
        expect(info.symbol).toBeDefined();
        expect(info.rate).toBeGreaterThan(0);
      }
    });
  });

  describe("CurrencyManager.getAllCurrencies", () => {
    it("should return array of all currency codes", () => {
      const currencies = CurrencyManager.getAllCurrencies();

      expect(Array.isArray(currencies)).toBe(true);
      expect(currencies.length).toBe(9); // GBP, USD, EUR, SAR, AED, QAR, KWD, BHD, OMR
    });

    it("should include all GCC currencies", () => {
      const currencies = CurrencyManager.getAllCurrencies();

      expect(currencies).toContain("SAR");
      expect(currencies).toContain("AED");
      expect(currencies).toContain("QAR");
      expect(currencies).toContain("KWD");
      expect(currencies).toContain("BHD");
      expect(currencies).toContain("OMR");
    });

    it("should include major international currencies", () => {
      const currencies = CurrencyManager.getAllCurrencies();

      expect(currencies).toContain("GBP");
      expect(currencies).toContain("USD");
      expect(currencies).toContain("EUR");
    });
  });

  describe("CurrencyManager.findStripePrice", () => {
    const mockPrices = [
      { id: "price_gbp_month", currency: "gbp", interval: "month" },
      { id: "price_gbp_year", currency: "gbp", interval: "year" },
      { id: "price_usd_month", currency: "usd", interval: "month" },
      { id: "price_sar_month", currency: "sar", interval: "month" },
    ];

    it("should find GBP monthly price", () => {
      const price = CurrencyManager.findStripePrice(mockPrices, "GBP", "month");

      expect(price).toBeDefined();
      expect(price?.id).toBe("price_gbp_month");
    });

    it("should find GBP yearly price", () => {
      const price = CurrencyManager.findStripePrice(mockPrices, "GBP", "year");

      expect(price).toBeDefined();
      expect(price?.id).toBe("price_gbp_year");
    });

    it("should find USD monthly price", () => {
      const price = CurrencyManager.findStripePrice(mockPrices, "USD", "month");

      expect(price).toBeDefined();
      expect(price?.id).toBe("price_usd_month");
    });

    it("should find SAR monthly price", () => {
      const price = CurrencyManager.findStripePrice(mockPrices, "SAR", "month");

      expect(price).toBeDefined();
      expect(price?.id).toBe("price_sar_month");
    });

    it("should return undefined if no match found", () => {
      const price = CurrencyManager.findStripePrice(mockPrices, "EUR", "month");

      expect(price).toBeUndefined();
    });

    it("should match currency case-insensitively", () => {
      const price = CurrencyManager.findStripePrice(mockPrices, "GBP", "month");

      expect(price).toBeDefined();
      expect(price?.currency).toBe("gbp"); // Stripe uses lowercase
    });

    it("should not match different intervals", () => {
      const price = CurrencyManager.findStripePrice(mockPrices, "USD", "year");

      expect(price).toBeUndefined(); // Only USD monthly exists
    });
  });

  describe("Edge Cases", () => {
    it("should handle very large amounts in conversion", () => {
      const converted = CurrencyManager.convert(1000000, "USD");
      expect(converted).toBe(1270000);
    });

    it("should handle negative amounts (refunds)", () => {
      const converted = CurrencyManager.convert(-100, "USD");
      expect(converted).toBe(-127);
    });

    it("should format negative amounts correctly", () => {
      const formatted = CurrencyManager.formatPrice(-100, "GBP");
      expect(formatted).toContain("£");
      expect(formatted).toContain("-100");
    });

    it("should handle Intl errors gracefully", () => {
      (global as any).window = {};
      Object.defineProperty(global, "Intl", {
        writable: true,
        value: undefined,
      });

      // Should default to GBP without throwing
      const currency = CurrencyManager.detectUserCurrency();
      expect(currency).toBe("GBP");
    });
  });

  describe("GCC Currency Support", () => {
    const gccTimezones = [
      { timezone: "Asia/Riyadh", expected: "SAR" },
      { timezone: "Asia/Dubai", expected: "AED" },
      { timezone: "Asia/Qatar", expected: "QAR" },
      { timezone: "Asia/Kuwait", expected: "KWD" },
      { timezone: "Asia/Bahrain", expected: "BHD" },
      { timezone: "Asia/Muscat", expected: "OMR" },
    ];

    it.each(gccTimezones)(
      "should detect $expected for $timezone",
      ({ timezone, expected }) => {
        (global as any).window = {};
        mockWindow(timezone);
        const currency = CurrencyManager.detectUserCurrency();
        expect(currency).toBe(expected);
      },
    );
  });

  describe("Exchange Rates", () => {
    it("should have positive rates for all currencies", () => {
      const currencies = CurrencyManager.getAllCurrencies();

      for (const currency of currencies) {
        const info = CurrencyManager.getCurrencyInfo(currency);
        expect(info.rate).toBeGreaterThan(0);
      }
    });

    it("should have GBP as base rate (1)", () => {
      const info = CurrencyManager.getCurrencyInfo("GBP");
      expect(info.rate).toBe(1);
    });

    it("should have unique symbols for each currency", () => {
      const currencies = CurrencyManager.getAllCurrencies();
      const symbols = currencies.map(
        (c) => CurrencyManager.getCurrencyInfo(c).symbol,
      );
      const uniqueSymbols = new Set(symbols);

      expect(uniqueSymbols.size).toBe(currencies.length);
    });
  });
});
