/**
 * Tests for Pricing Service localized pricing functions
 *
 * Tests:
 * - getLocalizedPricing with different currencies
 * - getTierLocalizedPricing for single tier lookup
 * - Exchange rate conversion accuracy
 * - Fallback behavior
 */

// Mock dependencies before imports
jest.mock("@/lib/stripe", () => ({
  stripe: {
    products: {
      list: jest.fn(),
    },
    prices: {
      list: jest.fn(),
    },
  },
}));

jest.mock("@/lib/cache", () => ({
  cache: {
    getOrSet: jest.fn(),
  },
}));

jest.mock("@/lib/services/exchange-rate.service", () => ({
  getExchangeRates: jest.fn(),
}));

import {
  getLocalizedPricing,
  getTierLocalizedPricing,
  getPricingProducts,
} from "@/lib/services/pricing.service";
import { getExchangeRates } from "@/lib/services/exchange-rate.service";
import { cache } from "@/lib/cache";

const mockGetExchangeRates = getExchangeRates as jest.MockedFunction<
  typeof getExchangeRates
>;
const mockCache = cache as jest.Mocked<typeof cache>;

describe("Pricing Service - Localized Pricing", () => {
  const mockProducts = [
    {
      tierId: "tier1" as const,
      stripeProductId: "prod_starter",
      name: "Starter",
      description: "Basic plan for small clinics",
      features: ["1 WhatsApp account", "Basic scheduling"],
      prices: [
        {
          id: "price_starter_monthly",
          currency: "gbp",
          unitAmount: 9900, // £99.00 in pence
          interval: "month" as const,
          intervalCount: 1,
        },
        {
          id: "price_starter_yearly",
          currency: "gbp",
          unitAmount: 99000, // £990.00 in pence
          interval: "year" as const,
          intervalCount: 1,
        },
      ],
    },
    {
      tierId: "tier2" as const,
      stripeProductId: "prod_pro",
      name: "Professional",
      description: "Pro plan for growing clinics",
      features: ["5 WhatsApp accounts", "Advanced scheduling", "Analytics"],
      prices: [
        {
          id: "price_pro_monthly",
          currency: "gbp",
          unitAmount: 29900, // £299.00 in pence
          interval: "month" as const,
          intervalCount: 1,
        },
      ],
    },
  ];

  const mockRates = {
    date: "2024-01-01",
    baseCurrency: "GBP" as const,
    rates: {
      gbp: 1,
      qar: 4.5,
      aed: 4.59,
      sar: 4.68,
      usd: 1.25,
      eur: 1.16,
    },
    source: "api" as const,
    fetchedAt: new Date().toISOString(),
  };

  beforeEach(() => {
    jest.clearAllMocks();

    // Mock cache to return products directly
    mockCache.getOrSet.mockImplementation(async (_key, fn) => {
      return fn();
    });

    // Mock exchange rates
    mockGetExchangeRates.mockResolvedValue(mockRates);
  });

  describe("getLocalizedPricing", () => {
    beforeEach(() => {
      // Mock getPricingProducts by mocking the cache
      mockCache.getOrSet.mockResolvedValue(mockProducts);
    });

    it("returns GBP pricing with exchange rate of 1", async () => {
      const result = await getLocalizedPricing("GBP");

      expect(result.currency).toBe("GBP");
      expect(result.exchangeRate).toBe(1);
      expect(result.products).toHaveLength(2);
    });

    it("converts prices to QAR correctly", async () => {
      const result = await getLocalizedPricing("QAR");

      expect(result.currency).toBe("QAR");
      expect(result.exchangeRate).toBe(4.5);

      // First product monthly price: £99 * 4.5 = QAR 445.50
      const starterMonthly = result.products[0].prices[0];
      expect(starterMonthly.originalGBP).toBe(99);
      expect(starterMonthly.amount).toBe(445.5);
      expect(starterMonthly.currency).toBe("QAR");
      expect(starterMonthly.symbol).toBe("ر.ق");
    });

    it("includes correct currency configuration", async () => {
      const result = await getLocalizedPricing("AED");

      expect(result.currencyConfig.code).toBe("AED");
      expect(result.currencyConfig.symbol).toBe("د.إ");
      expect(result.currencyConfig.isRTL).toBe(true);
    });

    it("preserves product structure during conversion", async () => {
      const result = await getLocalizedPricing("SAR");

      const product = result.products[0];
      expect(product.tierId).toBe("tier1");
      expect(product.stripeProductId).toBe("prod_starter");
      expect(product.name).toBe("Starter");
      expect(product.features).toEqual([
        "1 WhatsApp account",
        "Basic scheduling",
      ]);
    });

    it("handles products with multiple prices", async () => {
      const result = await getLocalizedPricing("USD");

      const starterPrices = result.products[0].prices;
      expect(starterPrices).toHaveLength(2);
      expect(starterPrices[0].interval).toBe("month");
      expect(starterPrices[1].interval).toBe("year");
    });

    it("includes source information from exchange rates", async () => {
      const result = await getLocalizedPricing("EUR");

      expect(result.ratesSource).toBe("api");
      expect(result.fetchedAt).toBeDefined();
    });

    it("handles unsupported currency by using rate of 1", async () => {
      // Mock rates without the requested currency in the rates object
      mockGetExchangeRates.mockResolvedValue({
        ...mockRates,
        rates: { gbp: 1, usd: 1.25 }, // Missing some currencies
      });

      // Use a valid currency that just happens not to have rate data
      const result = await getLocalizedPricing("USD");

      // Should use the rate from the mocked response
      expect(result.exchangeRate).toBe(1.25);
    });
  });

  describe("getTierLocalizedPricing", () => {
    beforeEach(() => {
      mockCache.getOrSet.mockResolvedValue(mockProducts);
    });

    it("returns single tier with localized pricing", async () => {
      const result = await getTierLocalizedPricing("tier1", "QAR");

      expect(result).not.toBeNull();
      expect(result?.tierId).toBe("tier1");
      expect(result?.prices[0].currency).toBe("QAR");
    });

    it("returns null for non-existent tier", async () => {
      const result = await getTierLocalizedPricing("tier99" as any, "GBP");

      expect(result).toBeNull();
    });

    it("defaults to GBP when no currency specified", async () => {
      const result = await getTierLocalizedPricing("tier2");

      expect(result?.prices[0].currency).toBe("GBP");
    });
  });

  describe("Price formatting", () => {
    beforeEach(() => {
      mockCache.getOrSet.mockResolvedValue(mockProducts);
    });

    it("formats GBP with pound symbol before amount", async () => {
      const result = await getLocalizedPricing("GBP");

      expect(result.products[0].prices[0].formatted).toBe("£99.00");
    });

    it("formats QAR with symbol after amount", async () => {
      const result = await getLocalizedPricing("QAR");

      expect(result.products[0].prices[0].formatted).toMatch(/\d+.*ر\.ق/);
    });

    it("rounds to 2 decimal places", async () => {
      // Mock a rate that would cause many decimal places
      mockGetExchangeRates.mockResolvedValue({
        ...mockRates,
        rates: { ...mockRates.rates, qar: 4.567891 },
      });

      const result = await getLocalizedPricing("QAR");
      const amount = result.products[0].prices[0].amount;

      // Should be rounded to 2 decimal places
      expect(amount.toString()).toMatch(/^\d+(\.\d{1,2})?$/);
    });
  });
});

describe("Integration: Pricing + Exchange Rates", () => {
  it("handles exchange rate service errors gracefully", async () => {
    mockCache.getOrSet.mockResolvedValue([
      {
        tierId: "tier1",
        stripeProductId: "prod_test",
        name: "Test",
        description: null,
        features: [],
        prices: [
          {
            id: "price_test",
            currency: "gbp",
            unitAmount: 10000,
            interval: "month",
            intervalCount: 1,
          },
        ],
      },
    ]);

    // Make exchange rate service fail
    mockGetExchangeRates.mockRejectedValue(
      new Error("Exchange rate service unavailable"),
    );

    // Should throw or handle gracefully depending on implementation
    await expect(getLocalizedPricing("QAR")).rejects.toThrow();
  });
});
