import { describe, it, expect, vi, beforeEach } from "vitest";
import {
  toCzk,
  getRate,
  __resetRateCacheForTest,
} from "../services/exchange-rates";

// Mock global fetch
const mockFetch = vi.fn();
global.fetch = mockFetch;

describe("exchange-rates", () => {
  beforeEach(() => {
    mockFetch.mockReset();
    // The rate cache is module-level and persists across tests. Without this
    // reset, the first test to populate "today" would short-circuit every
    // later mockFetch resolution (cache hit), making assertions order-dependent
    // and asserting stale rates. Reset it so each test gets a clean fetch.
    __resetRateCacheForTest();
  });

  describe("toCzk", () => {
    it("returns amount unchanged for CZK", async () => {
      const result = await toCzk(123.45, "CZK");
      expect(result).toBe(123.45);
    });

    it("throws for unknown currency when API fails and no cache", async () => {
      mockFetch.mockRejectedValue(new Error("Network error"));
      await expect(toCzk(100, "XYZ")).rejects.toThrow(
        /Nepodařilo se získat aktuální kurzy/,
      );
    });

    it("throws for unknown currency even when API succeeds", async () => {
      mockFetch.mockResolvedValue({
        ok: true,
        json: async () => ({
          rates: [{ currencyCode: "EUR", rate: 25, amount: 1 }],
        }),
      });
      await expect(toCzk(100, "XYZ")).rejects.toThrow(/Neznámá měna: XYZ/);
    });

    it("converts EUR using fetched rate", async () => {
      mockFetch.mockResolvedValue({
        ok: true,
        json: async () => ({
          rates: [{ currencyCode: "EUR", rate: 25, amount: 1 }],
        }),
      });
      const result = await toCzk(100, "EUR");
      expect(result).toBe(2500);
    });

    it("honours amount != 1 (currency quoted per 100 units)", async () => {
      // CNB quotes some currencies (e.g. HUF, JPY) per 100 units: the `rate`
      // is for `amount` units, so the per-unit rate is rate/amount. Here the
      // per-unit rate is 250/100 = 2.5 CZK; converting 100 units → 250 CZK.
      // If the service ignored `amount` it would compute 100*250 = 25000.
      mockFetch.mockResolvedValue({
        ok: true,
        json: async () => ({
          rates: [{ currencyCode: "HUF", rate: 250, amount: 100 }],
        }),
      });
      const result = await toCzk(100, "HUF");
      expect(result).toBe(250);
    });
  });

  describe("getRate", () => {
    it("returns 1 for CZK", async () => {
      const result = await getRate("CZK");
      expect(result).toBe(1);
    });

    it("throws for unknown currency", async () => {
      mockFetch.mockResolvedValue({
        ok: true,
        json: async () => ({
          rates: [{ currencyCode: "EUR", rate: 25, amount: 1 }],
        }),
      });
      await expect(getRate("XYZ")).rejects.toThrow(/Neznámá měna: XYZ/);
    });

    it("returns the per-unit rate for amount != 1", async () => {
      // rate 250 for amount 100 → per-unit rate 2.5 (CZK per 1 unit).
      mockFetch.mockResolvedValue({
        ok: true,
        json: async () => ({
          rates: [{ currencyCode: "HUF", rate: 250, amount: 100 }],
        }),
      });
      const result = await getRate("HUF");
      expect(result).toBe(2.5);
    });
  });
});
