/**
 * Reference pricing suite — proves the slim harness boots and exercises FareEngineService directly.
 * Also confirms matrix findings against the running engine:
 *   D1 — a promo with percentOff > 100 drives the total NEGATIVE (no clamp at 0).
 *   C1 ✅ FIXED (was 🔴 "missing USD→ETB FX rate is silently substituted with 1.0, fares
 *        collapse ~100x"): CurrencyService.getExchangeRate() now fails closed on a missing
 *        rate (see the "H-2: fail closed" comment in currency.service.ts, and
 *        pricing-currency.e2e-spec.ts's C2/C2b) — FareEngineService.calculate() calls it
 *        internally, so a missing rate now correctly rejects the fare calculation instead of
 *        silently underpricing it. Updated below to assert the current, correct behavior.
 */
import { FareEngineService } from "../src/modules/fare-engine/fare-engine.service";
import {
  createServiceHarness,
  ServiceHarness,
} from "./setup/slim-app";
import {
  IDS,
  resetAndSeedCore,
  DISTANCE,
  USD_TO_ETB,
} from "./fixtures/seed-core";

describe("Pricing — FareEngineService (slim harness)", () => {
  let harness: ServiceHarness;
  let fareEngine: FareEngineService;

  beforeAll(async () => {
    harness = await createServiceHarness();
    fareEngine = harness.moduleRef.get(FareEngineService);
  });

  afterAll(async () => {
    await harness?.close();
  });

  beforeEach(async () => {
    await resetAndSeedCore(harness.prisma);
  });

  // nationality 'Ethiopian' → LOCAL seat class (3.00 ETB/km) and ETB billing (rate 1).
  const baseDto = () => ({
    routeId: IDS.route,
    originStationId: IDS.stationA,
    destinationStationId: IDS.stationB,
    seatClassId: IDS.seatClassLocal,
    nationality: "Ethiopian",
    adultCount: 1,
    childCount: 0,
  });

  it("boots and computes a positive baseline fare (A→B, local, 1 adult)", async () => {
    const result = await fareEngine.calculate(baseDto() as any);
    // 100km × 3.00 ETB/km × 1 × USD_TO_ETB(100) = 30000 minor (see seat-class formula).
    expect(result.totalMinor).toBeGreaterThan(0);
    expect(result.totalMinor).toBe(DISTANCE.B * 3 * USD_TO_ETB);
  });

  it("D1 🔴 promo percentOff=150 produces a NEGATIVE total (no floor at 0)", async () => {
    await harness.prisma.promotion.create({
      data: {
        title: "Overshoot",
        code: "OVER150",
        percentOff: 150,
        validUntil: new Date(Date.now() + 86_400_000),
        active: true,
      },
    });

    const result = await fareEngine.calculate({
      ...baseDto(),
      promoCode: "OVER150",
    } as any);

    // Expected (correct) behavior: total clamped at >= 0. Actual: negative.
    expect(result.totalMinor).toBeLessThan(0);
  });

  it("D2 🔴 fixed amountOffMinor larger than subtotal drives total NEGATIVE", async () => {
    const base = await fareEngine.calculate(baseDto() as any); // 30000 minor
    await harness.prisma.promotion.create({
      data: {
        title: "Huge fixed",
        code: "FIXEDBIG",
        amountOffMinor: base.totalMinor + 10_000,
        validUntil: new Date(Date.now() + 86_400_000),
        active: true,
      },
    });

    const result = await fareEngine.calculate({
      ...baseDto(),
      promoCode: "FIXEDBIG",
    } as any);
    expect(result.totalMinor).toBeLessThan(0);
  });

  it("D4 🔴 promo with percentOff=0 is treated as FIXED (0 is falsy) and applies amountOffMinor", async () => {
    // A promo intended as '0% off' but also carrying a stray fixed amount: the falsy check
    // `promo.percentOff ? percent : amountOffMinor` wrongly applies the fixed discount.
    await harness.prisma.promotion.create({
      data: {
        title: "Zero percent",
        code: "ZERO0",
        percentOff: 0,
        amountOffMinor: 5000,
        validUntil: new Date(Date.now() + 86_400_000),
        active: true,
      },
    });

    const base = await fareEngine.calculate(baseDto() as any);
    const withPromo = await fareEngine.calculate({
      ...baseDto(),
      promoCode: "ZERO0",
    } as any);

    // A true 0% promo should not change the price; here it deducts the fixed 5000.
    expect(withPromo.totalMinor).toBe(base.totalMinor - 5000);
  });

  it("C1 ✅ a missing USD→ETB rate now rejects the fare calculation instead of silently pricing at parity", async () => {
    const withRate = await fareEngine.calculate(baseDto() as any);
    expect(withRate.totalMinor).toBeGreaterThan(0);

    // Remove the USD→ETB rate the seat-class formula multiplies by.
    await harness.prisma.currencyExchangeRate.deleteMany({
      where: { fromCurrency: "USD", toCurrency: "ETB" },
    });

    await expect(fareEngine.calculate(baseDto() as any)).rejects.toThrow(/No exchange rate configured/i);
  });
});
