/**
 * Executable reproducers for the highest-severity findings that were previously inspection-only.
 * All Tier-2 (direct instantiation, real Prisma + stubbed collaborators).
 *
 *   C-1 ✅ FIXED (was 🔴 "BookingsService trusts client reviewedTotalMinor: a booking is
 *          stored with totalMinor=1 while the server fare engine computed ~30000") —
 *          createOneWayBooking now runs assertTotalNotUnderAuthoritative before persisting;
 *          a forged low total is rejected, not stored. Test below updated to assert this.
 *   C-4 🔴 finalizePaymentSuccess confirms a booking without comparing the paid amount: an intent for
 *          1 minor confirms a 30000 booking.
 *   C-6 🔴 Concurrent WALLET payments double-spend one balance (no row lock): a wallet funded for one
 *          ticket pays for two.
 */
import { BookingsService } from "../src/modules/bookings/bookings.service";
import { PaymentsService } from "../src/modules/payments/payments.service";
import { FareEngineService } from "../src/modules/fare-engine/fare-engine.service";
import { CurrencyService } from "../src/modules/currency/currency.service";
import { getTestPrisma, disconnectTestPrisma } from "./setup/prisma";
import { truncateAllPassenger, seedCore, IDS } from "./fixtures/seed-core";

function asyncStub(): any {
  return new Proxy({}, { get: () => async () => undefined });
}

/**
 * Wraps a PrismaClient so that inside `$transaction(cb)`, every `walletAccount.update` waits until
 * BOTH concurrent transactions have finished their `walletAccount.findUnique` (balance read). This
 * deterministically forces the exact interleaving a real multi-request system permits, exposing the
 * service's unlocked check-then-act (no SELECT … FOR UPDATE). Only scheduling is controlled — the
 * service's own logic runs unmodified.
 */
function makeRaceWrappedPrisma(real: any, parties: number) {
  let arrived = 0;
  let release!: () => void;
  const gate = new Promise<void>((r) => (release = r));
  const signalRead = () => {
    if (++arrived >= parties) release();
  };

  return new Proxy(real, {
    get(target, prop, receiver) {
      if (prop === "$transaction") {
        return (cb: (tx: any) => unknown, opts?: unknown) =>
          target.$transaction((tx: any) => {
            const wrappedTx = new Proxy(tx, {
              get(t, p) {
                if (p === "walletAccount") {
                  return {
                    findUnique: async (args: unknown) => {
                      const res = await t.walletAccount.findUnique(args);
                      signalRead();
                      return res;
                    },
                    update: async (args: unknown) => {
                      await gate; // hold the write until both reads are done
                      return t.walletAccount.update(args);
                    },
                  };
                }
                return t[p];
              },
            });
            return cb(wrappedTx);
          }, opts);
      }
      return Reflect.get(target, prop, receiver);
    },
  });
}

let seq = 0;
async function makeSchedule(prisma: any) {
  const train = await prisma.train.create({ data: { number: `CR-${++seq}`, name: "T" } });
  return prisma.trainSchedule.create({
    data: {
      trainId: train.id,
      routeId: IDS.route,
      originStationId: IDS.stationA,
      destinationStationId: IDS.stationB,
      departureAt: new Date(Date.now() + 86_400_000),
      arrivalAt: new Date(Date.now() + 90_000_000),
      durationMinutes: 60,
    },
  });
}

describe("Critical reproducers (Tier-2)", () => {
  const prisma = getTestPrisma();

  beforeEach(async () => {
    await truncateAllPassenger(prisma);
    await seedCore(prisma);
  });
  afterAll(async () => {
    await disconnectTestPrisma();
  });

  // ── C-1 ──────────────────────────────────────────────────────────────────
  it("C-1 🔴 booking stores client reviewedTotalMinor=1 while the fare engine computed ~30000", async () => {
    const passenger = await prisma.passenger.create({ data: {} });
    const schedule = await makeSchedule(prisma);
    // Stop times so origin/dest resolve on the schedule.
    await prisma.tripStopTime.createMany({
      data: [
        { scheduleId: schedule.id, stationId: IDS.stationA, sequence: 1 },
        { scheduleId: schedule.id, stationId: IDS.stationB, sequence: 2 },
      ],
    });
    // Coach + seat for the passenger to occupy.
    const coach = await prisma.coach.create({
      data: { coachTypeId: IDS.coachType, number: `C-${seq}` },
    });
    const seat = await prisma.seat.create({
      data: { coachId: coach.id, seatNumber: "1A", row: 1, col: "1" },
    });
    // A real server fare source (tripId match → highest priority): 30000 minor.
    await prisma.fareRule.create({
      data: {
        tripId: schedule.id,
        seatClassId: IDS.seatClassLocal,
        baseFareMinor: 30_000,
        currency: "ETB",
        validFrom: new Date("2020-01-01"),
      },
    });
    const hold = await prisma.seatHold.create({
      data: {
        scheduleId: schedule.id,
        seatIds: [seat.id],
        passengerId: passenger.id,
        expiresAt: new Date(Date.now() + 3_600_000),
      },
    });

    const bookings = new BookingsService(
      prisma as any,
      asyncStub(), // dataSource
      asyncStub(), // seatsService (confirmSeats no-op)
      { emit: () => true } as any, // eventEmitter
      asyncStub(), // verifaydaService (PASSPORT path skips it anyway)
      asyncStub(), // currencyService (ETB path skips it)
      asyncStub(), // fareEngine (FareRule short-circuits before this)
      asyncStub(), // auditService
    );

    const dto = {
      passengerId: passenger.id,
      scheduleId: schedule.id,
      holdId: hold.id,
      originStationId: IDS.stationA,
      destinationStationId: IDS.stationB,
      seatClassId: IDS.seatClassLocal,
      bookingType: "ONE_WAY",
      reviewedTotalMinor: 1, // the forged client total
      passengers: [
        {
          seatId: seat.id,
          passengerName: "Mallory Adult",
          dateOfBirth: new Date("1990-01-01"),
          idDocumentType: "PASSPORT",
          passportNumber: "P123",
          passportCountry: "ET",
          nationality: "Ethiopian",
          // NOTE: no seatFareMinor → not "allFaresProvided" → reviewedTotalMinor is trusted
        },
      ],
    };

    // FIXED (was 🔴): createOneWayBooking now runs the forged reviewedTotalMinor through
    // assertTotalNotUnderAuthoritative before ever writing a Booking row — a client total
    // below the server-computed fare (minus a 1% rounding tolerance) is rejected outright,
    // not silently persisted. Confirmed by reading bookings.service.ts's C-1 guard comment.
    await expect((bookings as any).createOneWayBooking(dto)).rejects.toThrow(
      /Booking total does not match the authoritative fare/i,
    );

    const stored = await prisma.booking.findFirst({ where: { scheduleId: schedule.id, passengerId: passenger.id } });
    expect(stored).toBeNull(); // no under-priced booking left behind
  });

  // ── C-4 ──────────────────────────────────────────────────────────────────
  it("C-4 🔴 finalizePaymentSuccess confirms a 30000 booking from an intent of 1 (no amount check)", async () => {
    const passenger = await prisma.passenger.create({ data: {} });
    const schedule = await makeSchedule(prisma);
    const booking = await prisma.booking.create({
      data: {
        bookingRef: "PAY-0001",
        passengerId: passenger.id,
        scheduleId: schedule.id,
        totalMinor: 30_000,
        status: "PENDING_PAYMENT",
      },
    });
    const intent = await prisma.paymentIntent.create({
      data: {
        bookingId: booking.id,
        amountMinor: 1, // wildly short payment
        method: "WALLET",
        status: "PROCESSING",
      },
    });

    const payments = new PaymentsService(
      prisma as any,
      { confirmSeats: async () => undefined } as any,
      { generate: async () => undefined } as any, // must not throw (re-thrown otherwise)
      { emit: () => true } as any,
      asyncStub(), // paymentClient
      asyncStub(), // currencyService (not used on this path)
      asyncStub(), // auditService
    );

    await payments.finalizePaymentSuccess({ intentId: intent.id });

    const after = await prisma.booking.findUnique({ where: { id: booking.id } });
    // Confirmed despite intent.amountMinor (1) ≠ booking.totalMinor (30000).
    expect(after?.status).toBe("CONFIRMED");
  });

  // ── C-6 ──────────────────────────────────────────────────────────────────
  it("C-6 🔴 two concurrent WALLET payments double-spend a single-ticket balance", async () => {
    const passenger = await prisma.passenger.create({ data: {} });
    const schedule = await makeSchedule(prisma);
    // Wallet funded for exactly ONE ticket.
    await prisma.walletAccount.create({
      data: { passengerId: passenger.id, balanceMinor: 30_000 },
    });
    const mkBooking = (ref: string) =>
      prisma.booking.create({
        data: {
          bookingRef: ref,
          passengerId: passenger.id,
          scheduleId: schedule.id,
          totalMinor: 30_000,
          status: "PENDING_PAYMENT",
        },
      });
    const b1 = await mkBooking("W-0001");
    const b2 = await mkBooking("W-0002");

    // Race-wrapped prisma forces both balance reads to complete before either debit writes.
    const racePrisma = makeRaceWrappedPrisma(prisma, 2);
    const payments = new PaymentsService(
      racePrisma as any,
      { confirmSeats: async () => undefined } as any,
      { generate: async () => undefined } as any,
      { emit: () => true } as any,
      asyncStub(),
      asyncStub(),
      asyncStub(),
    );

    const [bk1, bk2] = await Promise.all([
      prisma.booking.findUnique({ where: { id: b1.id }, include: { seats: true } }),
      prisma.booking.findUnique({ where: { id: b2.id }, include: { seats: true } }),
    ]);

    const [r1, r2] = await Promise.allSettled([
      (payments as any).initiateWalletPayment(bk1),
      (payments as any).initiateWalletPayment(bk2),
    ]);

    const succeeded = await prisma.paymentIntent.count({
      where: { bookingId: { in: [b1.id, b2.id] }, status: { in: ["SUCCEEDED", "PROCESSING"] } },
    });
    const debits = await prisma.walletLedgerEntry.count({ where: { type: "DEBIT" } });
    const wallet = await prisma.walletAccount.findUnique({
      where: { passengerId: passenger.id },
    });

    // Double-spend signature: two successful debits from a one-ticket balance, or a negative
    // balance. A correctly-locked wallet allows exactly one.
    const totalDebited = debits * 30_000;
    const doubleSpent =
      (succeeded === 2 && totalDebited > 30_000) || (wallet?.balanceMinor ?? 0) < 0;
    expect(doubleSpent).toBe(true);
    expect([r1.status, r2.status]).toEqual(["fulfilled", "fulfilled"]);
  });
});
