/**
 * C-9-UI 🔴 Authenticated POST /bookings is broken: the controller overrides passengerId with the
 * JWT user id (`bookings.controller.ts:528-532`, "never trust the request body"), but the service
 * only resolves an iamUserId → Passenger when it is NON-UUID (`bookings.service.ts:773`). Real IAM
 * ids are UUIDs, and registration creates `Passenger.id ≠ iamUserId` (`passenger-auth.service.ts:225`),
 * so `booking.create` uses the iamUserId directly as passengerId → foreign-key violation.
 *
 * This reproduces the controller's behavior by calling BookingsService.create with passengerId set to
 * a UUID iamUserId (not the Passenger.id), exactly as the authed controller does. It also shows the
 * CONTROL: passing the real Passenger.id succeeds — proving the resolution gap, not a fixture problem.
 */
import { BookingsService } from "../src/modules/bookings/bookings.service";
import { getTestPrisma, disconnectTestPrisma } from "./setup/prisma";
import { truncateAllPassenger, seedCore, IDS } from "./fixtures/seed-core";

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

let seq = 0;
async function buildBookableGraph(prisma: any, passengerId: string, iamUserId: string) {
  const passenger = await prisma.passenger.create({ data: { id: passengerId, iamUserId } });
  const train = await prisma.train.create({ data: { number: `AB-${++seq}`, name: "T" } });
  const schedule = await 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,
    },
  });
  await prisma.tripStopTime.createMany({
    data: [
      { scheduleId: schedule.id, stationId: IDS.stationA, sequence: 1 },
      { scheduleId: schedule.id, stationId: IDS.stationB, sequence: 2 },
    ],
  });
  const coach = await prisma.coach.create({ data: { coachTypeId: IDS.coachType, number: `AB-${seq}` } });
  const seat = await prisma.seat.create({ data: { coachId: coach.id, seatNumber: "1A", row: 1, col: "1" } });
  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, expiresAt: new Date(Date.now() + 3_600_000) },
  });
  return { schedule, seat, hold };
}

function dtoFor(passengerId: string, schedule: any, hold: any, seat: any) {
  return {
    passengerId, // the controller passes req.user.id here (the iamUserId)
    scheduleId: schedule.id,
    holdId: hold.id,
    originStationId: IDS.stationA,
    destinationStationId: IDS.stationB,
    seatClassId: IDS.seatClassLocal,
    bookingType: "ONE_WAY",
    passengers: [
      {
        seatId: seat.id,
        passengerName: "Auth User",
        dateOfBirth: new Date("1990-01-01"),
        idDocumentType: "PASSPORT",
        passportNumber: "P1",
        passportCountry: "ET",
        nationality: "Ethiopian",
        seatFareMinor: 30_000,
      },
    ],
  };
}

describe("Authenticated booking passengerId resolution (regression)", () => {
  const prisma = getTestPrisma();
  let bookings: BookingsService;

  beforeAll(() => {
    bookings = new BookingsService(
      prisma as any,
      { query: async () => [] } as any, // dataSource (resolveIamContact raw SQL → [])
      asyncStub(), // seatsService
      asyncStub(), // ticketsService — constructor gained this param since this test was written
      { emit: () => true } as any,
      asyncStub(), // verifaydaService (PASSPORT skips)
      asyncStub(), // currencyService (ETB skips)
      asyncStub(), // fareEngine (FareRule short-circuits)
      asyncStub(), // auditService
    );
  });
  beforeEach(async () => {
    await truncateAllPassenger(prisma);
    await seedCore(prisma);
  });
  afterAll(async () => {
    await disconnectTestPrisma();
  });

  it("🔴 create() with a UUID iamUserId (as the authed controller passes) FAILS the passenger FK", async () => {
    const passengerId = "aaaaaaaa-0000-4000-8000-000000000001"; // real Passenger.id
    const iamUserId = "bbbbbbbb-0000-4000-8000-000000000002"; // UUID iamUserId ≠ Passenger.id
    const { schedule, hold, seat } = await buildBookableGraph(prisma, passengerId, iamUserId);

    // The controller calls service.create({ ...dto, passengerId: req.user.id }) — i.e. the iamUserId.
    await expect(
      bookings.create(dtoFor(iamUserId, schedule, hold, seat) as any),
    ).rejects.toThrow(); // Prisma P2003 on Booking_passengerId_fkey

    expect(await prisma.booking.count()).toBe(0);
  });

  it("control: create() with the real Passenger.id succeeds — proving the gap is the id, not the fixture", async () => {
    const passengerId = "aaaaaaaa-0000-4000-8000-000000000003";
    const iamUserId = "bbbbbbbb-0000-4000-8000-000000000004";
    const { schedule, hold, seat } = await buildBookableGraph(prisma, passengerId, iamUserId);

    const booking: any = await bookings.create(dtoFor(passengerId, schedule, hold, seat) as any);
    expect(booking.id).toBeTruthy();
    expect(booking.passengerId).toBe(passengerId);
  });
});
