/**
 * Deterministic core fixtures for the pricing E2E suites.
 *
 * The repo's `prisma/seed.ts` is entirely commented out (every step disabled), so the harness
 * builds its own minimal, fully-controlled graph: coach type → seat classes → stations → route
 * with distance-bearing stops → FX rates. Fixed UUIDs let specs reference entities directly.
 *
 * Uses a bare PrismaClient (not the Nest PrismaService) so it can run in jest globalSetup or
 * inside a spec without booting the app. Reads DATABASE_URL from process.env (load-env sets it).
 */
import { PrismaClient } from "@prisma/client";

export const IDS = {
  coachType: "00000000-0000-4000-8000-000000000001",
  seatClassLocal: "00000000-0000-4000-8000-000000000010",
  seatClassIntl: "00000000-0000-4000-8000-000000000011",
  stationA: "00000000-0000-4000-8000-000000000020",
  stationB: "00000000-0000-4000-8000-000000000021",
  stationC: "00000000-0000-4000-8000-000000000022",
  route: "00000000-0000-4000-8000-000000000030",
} as const;

/** Route stop distances (km from origin). A=0, B=100, C=250 → A→B is 100km, A→C is 250km. */
export const DISTANCE = { A: 0, B: 100, C: 250 } as const;

/**
 * Optional per-stop check-in-cutoff/travel-time overrides, keyed by station label (A/B/C).
 * Lets a spec seed a distinct `checkinMinutesBefore` override and/or `travelMinutesToStop`
 * per stop without changing the zero-arg call sites the other specs rely on.
 */
export interface RouteStopOverrides {
  A?: { checkinMinutesBefore?: number; travelMinutesToStop?: number };
  B?: { checkinMinutesBefore?: number; travelMinutesToStop?: number };
  C?: { checkinMinutesBefore?: number; travelMinutesToStop?: number };
}

/**
 * FX rate chosen so the seat-class distance formula (which multiplies an ETB/km rate by the
 * USD→ETB rate — see fare-engine.service.ts:157) yields whole ETB-minor amounts. 100 makes the
 * major→minor scaling line up; a realistic rate (e.g. 132) would visibly distort domestic fares,
 * which is itself a finding the suites probe.
 */
export const USD_TO_ETB = 100;
export const ETB_TO_DJF = 1.8;

/** TRUNCATE every table in the `passenger` schema (except Prisma's migration bookkeeping). */
export async function truncateAllPassenger(prisma: PrismaClient): Promise<void> {
  const rows = await prisma.$queryRawUnsafe<Array<{ tablename: string }>>(
    `SELECT tablename FROM pg_tables WHERE schemaname = 'passenger' AND tablename <> '_prisma_migrations'`,
  );
  if (rows.length === 0) return;
  const list = rows.map((r) => `passenger."${r.tablename}"`).join(", ");
  await prisma.$executeRawUnsafe(
    `TRUNCATE ${list} RESTART IDENTITY CASCADE`,
  );
}

/** Insert the deterministic core graph. Call after truncateAllPassenger. */
export async function seedCore(prisma: PrismaClient, stopOverrides: RouteStopOverrides = {}): Promise<void> {
  const past = new Date("2020-01-01T00:00:00.000Z");

  await prisma.coachType.create({
    data: {
      id: IDS.coachType,
      code: "STD",
      name: "Standard Coach",
      type: "passenger",
    },
  });

  // LOCAL and INTERNATIONAL seat classes share the coach type + bedPosition (null = regular seat),
  // which is exactly how fare-engine picks the nationality-matched class (findFirst on those keys).
  await prisma.seatClass.createMany({
    data: [
      {
        id: IDS.seatClassLocal,
        coachTypeId: IDS.coachType,
        name: "Local Standard",
        nationalityType: "LOCAL",
        bedPosition: null,
        baseFareMinor: 300, // 3.00 ETB/km
        premiumMinor: 0,
        insuranceFeeMinor: 0,
        isActive: true,
      },
      {
        id: IDS.seatClassIntl,
        coachTypeId: IDS.coachType,
        name: "Intl Standard",
        nationalityType: "INTERNATIONAL",
        bedPosition: null,
        baseFareMinor: 500, // 5.00 ETB/km
        premiumMinor: 0,
        insuranceFeeMinor: 0,
        isActive: true,
      },
    ],
  });

  await prisma.station.createMany({
    data: [
      { id: IDS.stationA, code: "AAA", name: "Alpha", city: "Alpha City", sequence: 1 },
      { id: IDS.stationB, code: "BBB", name: "Bravo", city: "Bravo City", sequence: 2 },
      { id: IDS.stationC, code: "CCC", name: "Charlie", city: "Charlie City", sequence: 3 },
    ],
  });

  await prisma.route.create({
    data: {
      id: IDS.route,
      code: "RT-MAIN",
      name: "Main Line",
      effectiveFrom: past,
      active: true,
      stops: {
        create: [
          { stationId: IDS.stationA, sequence: 1, distanceKm: DISTANCE.A, ...stopOverrides.A },
          { stationId: IDS.stationB, sequence: 2, distanceKm: DISTANCE.B, ...stopOverrides.B },
          { stationId: IDS.stationC, sequence: 3, distanceKm: DISTANCE.C, ...stopOverrides.C },
        ],
      },
    },
  });

  await prisma.currencyExchangeRate.createMany({
    data: [
      { fromCurrency: "USD", toCurrency: "ETB", rate: USD_TO_ETB, effectiveDate: new Date() },
      { fromCurrency: "ETB", toCurrency: "USD", rate: 1 / USD_TO_ETB, effectiveDate: new Date() },
      { fromCurrency: "ETB", toCurrency: "DJF", rate: ETB_TO_DJF, effectiveDate: new Date() },
      { fromCurrency: "DJF", toCurrency: "ETB", rate: 1 / ETB_TO_DJF, effectiveDate: new Date() },
    ],
  });
}

/** Convenience: reset + seed in one call. */
export async function resetAndSeedCore(prisma: PrismaClient, stopOverrides: RouteStopOverrides = {}): Promise<void> {
  await truncateAllPassenger(prisma);
  await seedCore(prisma, stopOverrides);
}
