import { PrismaClient } from '@prisma/client';
import * as bcrypt from 'bcrypt';
import { randomUUID as uuidv4 } from 'crypto';

const prisma = new PrismaClient();

const TRAIN_ID = uuidv4();

async function seedSystemUsers() {
  console.log('👥 Seeding system users...');
  const adminHash = await bcrypt.hash('admin123', 10);
  const passengerHash = await bcrypt.hash('password123', 10);
  const agentHash = await bcrypt.hash('agent123', 10);
  const supervisorHash = await bcrypt.hash('supervisor123', 10);
  const staffHash = await bcrypt.hash('staff123', 10);

  const admin = await prisma.user.upsert({
    where: { email: 'admin@edr-platform.com' },
    update: { passwordHash: adminHash, role: 'ADMIN' },
    create: {
      fullName: 'System Admin',
      email: 'admin@edr-platform.com',
      phone: '+251900000000',
      passwordHash: adminHash,
      role: 'ADMIN',
      gender: 'Male',
      dateOfBirth: new Date('1980-05-20'),
      nationality: 'Ethiopian',
      nationalId: 'ET123456789',
    },
  });
  console.log('  ✅ Admin: admin@edr-platform.com / admin123');

  const passenger = await prisma.user.upsert({
    where: { email: 'kelemu@email.com' },
    update: { passwordHash: passengerHash },
    create: {
      fullName: 'Kelemu Kebede',
      email: 'kelemu@email.com',
      phone: '+251911234567',
      passwordHash: passengerHash,
      role: 'PASSENGER',
      nationality: 'Ethiopian',
      faydaVerified: true,
      gender: 'Male',
      dateOfBirth: new Date('1990-03-15'),
      nationalId: 'ET987654321',
    },
  });

  let passengerRecord = await prisma.passenger.findUnique({ where: { userId: passenger.id } });
  if (!passengerRecord) {
    passengerRecord = await prisma.passenger.create({ data: { userId: passenger.id } });
    await prisma.loyaltyAccount.create({
      data: { passengerId: passengerRecord.id, pointsBalance: 1500, lifetimePoints: 3000, tier: 'SILVER' },
    });
    await prisma.walletAccount.create({
      data: { passengerId: passengerRecord.id, balanceMinor: 500 },
    });
  }
  await prisma.userPreferences.upsert({
    where: { userId: passenger.id },
    update: {},
    create: { userId: passenger.id, language: 'en' },
  });
  console.log('  ✅ Passenger: kelemu@email.com / password123');

  const agent = await prisma.user.upsert({
    where: { email: 'agent@edr-platform.com' },
    update: { passwordHash: agentHash, role: 'AGENT' },
    create: {
      fullName: 'Booking Agent',
      email: 'agent@edr-platform.com',
      phone: '+251911111111',
      passwordHash: agentHash,
      role: 'AGENT',
      gender: 'Female',
      dateOfBirth: new Date('1992-07-22'),
    },
  });
  await prisma.agent.upsert({
    where: { userId: agent.id },
    update: {},
    create: { userId: agent.id, agentCode: 'AG0001', commissionRate: 5 },
  });
  console.log('  ✅ Agent: agent@edr-platform.com / agent123');

  const supervisor = await prisma.user.upsert({
    where: { email: 'supervisor@edr-platform.com' },
    update: { passwordHash: supervisorHash, role: 'SUPERVISOR' },
    create: {
      fullName: 'System Supervisor',
      email: 'supervisor@edr-platform.com',
      phone: '+251922222222',
      passwordHash: supervisorHash,
      role: 'SUPERVISOR',
      gender: 'Male',
      dateOfBirth: new Date('1985-11-10'),
    },
  });
  console.log('  ✅ Supervisor: supervisor@edr-platform.com / supervisor123');

  const staff = await prisma.user.upsert({
    where: { email: 'staff@edr-platform.com' },
    update: { passwordHash: staffHash, role: 'STAFF' },
    create: {
      fullName: 'Support Staff',
      email: 'staff@edr-platform.com',
      phone: '+251933333333',
      passwordHash: staffHash,
      role: 'STAFF',
      gender: 'Female',
      dateOfBirth: new Date('1995-09-08'),
    },
  });
  console.log('  ✅ Staff: staff@edr-platform.com / staff123');
}

async function seedStations() {
  console.log('\n📍 Seeding 15 stations (Ethio-Djibouti Railway)...');
  const stations = [
    { code: 'SBT', name: 'Sebeta', city: 'Sebeta', countryCode: 'ET', lat: 8.9520, lng: 38.6150, sequence: 1 },
    { code: 'LEB', name: 'Lebu', city: 'Lebu', countryCode: 'ET', lat: 8.8890, lng: 38.5320, sequence: 2 },
    { code: 'BSH', name: 'Bishoftu', city: 'Bishoftu', countryCode: 'ET', lat: 8.7650, lng: 39.0240, sequence: 3 },
    { code: 'MOJ', name: 'Mojo', city: 'Mojo', countryCode: 'ET', lat: 8.6780, lng: 39.2130, sequence: 4 },
    { code: 'ADM', name: 'Adama', city: 'Adama', countryCode: 'ET', lat: 8.5420, lng: 39.2780, sequence: 5 },
    { code: 'MTE', name: 'Metehara', city: 'Metehara', countryCode: 'ET', lat: 8.7890, lng: 39.8920, sequence: 6 },
    { code: 'MIS', name: 'Mieso', city: 'Mieso', countryCode: 'ET', lat: 8.9120, lng: 40.3450, sequence: 7 },
    { code: 'BIK', name: 'Bike', city: 'Bike', countryCode: 'ET', lat: 9.1230, lng: 40.8670, sequence: 8 },
    { code: 'DRE', name: 'Dire Dawa', city: 'Dire Dawa', countryCode: 'ET', lat: 9.5915, lng: 41.8578, sequence: 9 },
    { code: 'ADG', name: 'Adigala', city: 'Adigala', countryCode: 'ET', lat: 9.7340, lng: 42.2150, sequence: 10 },
    { code: 'AYS', name: 'Aysha', city: 'Aysha', countryCode: 'ET', lat: 10.0120, lng: 42.5670, sequence: 11 },
    { code: 'DAW', name: 'Dawanle', city: 'Dawanle', countryCode: 'ET', lat: 10.2340, lng: 42.8340, sequence: 12 },
    { code: 'ALS', name: 'Alisabieh', city: 'Alisabieh', countryCode: 'DJ', lat: 10.8950, lng: 42.9560, sequence: 13 },
    { code: 'HOL', name: 'Holhol', city: 'Holhol', countryCode: 'DJ', lat: 11.1230, lng: 43.0450, sequence: 14 },
    { code: 'NAG', name: 'Nagad', city: 'Nagad', countryCode: 'DJ', lat: 11.3780, lng: 43.1200, sequence: 15 },
  ];

  for (const station of stations) {
    await prisma.station.upsert({
      where: { code: station.code },
      update: {},
      create: {
        id: uuidv4(),
        ...station,
      },
    });
  }
  console.log(`  ✅ ${stations.length} stations created`);
  return stations;
}

async function seedCoachTypesAndClasses() {
  console.log('\n🚂 Seeding coach types and seat classes...');
  const coachTypes = [
    { code: 'HSC', name: 'Hard Seat Coach', type: 'Economy Regular' },
    { code: 'HBC', name: 'Hard Berth Coach', type: 'Economy Bed' },
    { code: 'SBC', name: 'Soft Berth Coach', type: 'VIP Bed' },
  ];

  for (const ct of coachTypes) {
    await prisma.coachType.upsert({
      where: { id: ct.code },
      update: {},
      create: { ...ct, id: ct.code },
    });
  }

  const seatClasses = [
    { name: 'VIP Bed Lower', coachCode: 'SBC', baseFareMinor: 900, premiumMinor: 50, insuranceFeeMinor: 25 },
    { name: 'VIP Bed Upper', coachCode: 'SBC', baseFareMinor: 800, premiumMinor: 45, insuranceFeeMinor: 20 },
    { name: 'Economy Bed Upper', coachCode: 'HBC', baseFareMinor: 600, premiumMinor: 30, insuranceFeeMinor: 15 },
    { name: 'Economy Bed Middle', coachCode: 'HBC', baseFareMinor: 550, premiumMinor: 28, insuranceFeeMinor: 14 },
    { name: 'Economy Bed Lower', coachCode: 'HBC', baseFareMinor: 500, premiumMinor: 25, insuranceFeeMinor: 12 },
    { name: 'Economy Regular', coachCode: 'HSC', baseFareMinor: 250, premiumMinor: 12, insuranceFeeMinor: 6 },
  ];

  for (const sc of seatClasses) {
    const ct = await prisma.coachType.findUnique({ where: { id: sc.coachCode } });
    await prisma.seatClass.upsert({
      where: { coachTypeId_name: { coachTypeId: ct!.id, name: sc.name } },
      update: {},
      create: { coachTypeId: ct!.id, name: sc.name, baseFareMinor: sc.baseFareMinor, premiumMinor: sc.premiumMinor, insuranceFeeMinor: sc.insuranceFeeMinor },
    });
  }
  console.log(`  ✅ ${coachTypes.length} coach types, ${seatClasses.length} seat classes created`);
}

async function seedRoute() {
  console.log('\n🛣️ Seeding route and stops...');

  const route = await prisma.route.upsert({
    where: { code: 'Route-101' },
    update: {},
    create: {
      code: 'Route-101',
      name: 'Sebeta - Dire Dawa',
      description: 'Outbound local route from Sebeta to Dire Dawa',
      effectiveFrom: new Date('2026-01-01'),
      effectiveUntil: new Date('2034-12-31'),
      active: true,
    },
  });

  const stationCodes = ['SBT', 'LEB', 'BSH', 'MOJ', 'ADM', 'MTE', 'MIS', 'BIK', 'DRE'];
  const routeDistancesKm = [0, 11.5, 67.2, 89.9, 106.7, 180.2, 231.6, 293.6, 413.0];
  for (let i = 0; i < stationCodes.length; i++) {
    const station = await prisma.station.findUnique({ where: { code: stationCodes[i] } });
    await prisma.routeStop.upsert({
      where: { routeId_sequence: { routeId: route.id, sequence: i + 1 } },
      update: {},
      create: { routeId: route.id, stationId: station!.id, sequence: i + 1, distanceKm: routeDistancesKm[i] },
    });
  }

  const returnRoute = await prisma.route.upsert({
    where: { code: 'Route-102' },
    update: {},
    create: {
      code: 'Route-102',
      name: 'Dire Dawa - Sebeta',
      description: 'Inbound local route from Dire Dawa to Sebeta',
      effectiveFrom: new Date('2026-01-01'),
      effectiveUntil: new Date('2034-12-31'),
      active: true,
    },
  });

  const returnStationCodes = ['DRE', 'BIK', 'MIS', 'MTE', 'ADM', 'MOJ', 'BSH', 'LEB', 'SBT'];
  const returnRouteDistancesKm = [0, 119.4, 181.4, 232.8, 306.3, 323.1, 345.8, 401.5, 413.0];
  for (let i = 0; i < returnStationCodes.length; i++) {
    const station = await prisma.station.findUnique({ where: { code: returnStationCodes[i] } });
    await prisma.routeStop.upsert({
      where: { routeId_sequence: { routeId: returnRoute!.id, sequence: i + 1 } },
      update: {},
      create: { routeId: returnRoute!.id, stationId: station!.id, sequence: i + 1, distanceKm: returnRouteDistancesKm[i] },
    });
  }
  console.log(`  ✅ Route with ${returnStationCodes.length} stops created`);
}

async function seedCoaches() {
  console.log('\n🚃 Seeding coaches and seats...');
  const ecoCoachType = await prisma.coachType.findUnique({ where: { id: 'HSC' } });
  const ecoBedCoachType = await prisma.coachType.findUnique({ where: { id: 'HBC' } });
  const vipBedCoachType = await prisma.coachType.findUnique({ where: { id: 'SBC' } });

  const coaches = [
    { number: 'HSC-0001', coachTypeId: ecoCoachType!.id, arrangement: '3+2', capacity: 128, sequence: 1 },
    { number: 'HBC-0001', coachTypeId: ecoBedCoachType!.id, arrangement: '3+0', capacity: 66, sequence: 2 },
    { number: 'SBC-0001', coachTypeId: vipBedCoachType!.id, arrangement: '2+0', capacity: 40, sequence: 3 },
  ];

  let totalSeats = 0;
  for (const coach of coaches) {
    const c = await prisma.coach.upsert({
      where: { number: coach.number },
      update: {},
      create: coach,
    });

    // Idempotently reconcile the coach's seats. Upsert keyed on the
    // @@unique([coachId, row, col]) constraint so a re-seed updates existing
    // rows in place instead of deleting them. Deleting Seats fails with a P2003
    // FK violation once BookingSeat/SeatBlock/TicketSeat rows reference them.
    let seatIndex = 1;
    for (let row = 1; row <= Math.ceil(coach.capacity / 2); row++) {
      for (const col of ['A', 'B', 'C', 'D', 'E']) {
        if (seatIndex > coach.capacity) break;
        let bedPosition: string | null = null;
        if (c.coachTypeId === ecoBedCoachType!.id) {
          // Economy Bed: 3-row cycle (upper, middle, lower)
          if (row % 3 === 1) bedPosition = 'upper';
          else if (row % 3 === 2) bedPosition = 'middle';
          else bedPosition = 'lower';
        } else if (c.coachTypeId === vipBedCoachType!.id) {
          // VIP Bed: 2-row cycle (upper, lower)
          bedPosition = row % 2 === 1 ? 'upper' : 'lower';
        }

        const seatData = {
          seatNumber: seatIndex.toString(),
          isWindow: col === 'A' || col === 'E',
          isAisle: col === 'B' || col === 'C' || col === 'D',
          bedPosition,
        };

        await prisma.seat.upsert({
          where: { coachId_row_col: { coachId: c.id, row, col } },
          update: seatData,
          create: { coachId: c.id, row, col, ...seatData },
        });
        seatIndex++;
      }
    }
    totalSeats += coach.capacity;
  }
  console.log(`  ✅ ${coaches.length} coaches with ${totalSeats} seats created`);
}

async function seedTrips() {
  console.log('\n🚆 Seeding train service, schedule, and trips...');
  const train = await prisma.train.upsert({
    where: { number: 'EDR-001' },
    update: {},
    create: { id: TRAIN_ID, number: 'EDR-001', name: 'Express Service' },
  });

  const route = await prisma.route.findUnique({ where: { code: 'Route-101' } });
  const returnRoute = await prisma.route.findUnique({ where: { code: 'Route-102' } });
  const firstStation = await prisma.station.findUnique({ where: { code: 'SBT' } });
  const lastStation = await prisma.station.findUnique({ where: { code: 'DRE' } });
  const firstReturnStation = await prisma.station.findUnique({ where: { code: 'DRE' } });
  const lastReturnStation = await prisma.station.findUnique({ where: { code: 'SBT' } });
  const coaches = await prisma.coach.findMany();

  const now = new Date();
  const tomorrow = new Date(now);
  tomorrow.setDate(now.getDate() + 1);

  const schedules = [];

  for (let d = 0; d < 5; d++) {
    const tripDate = new Date(now);
    tripDate.setDate(tripDate.getDate() + d);
    tripDate.setHours(20, 30, 0, 0);
    schedules.push({
      trainId: train.id,
      routeId: route!.id,
      originStationId: firstStation!.id,
      destinationStationId: lastStation!.id,
      departureAt: new Date(tripDate),
      arrivalAt: new Date(tripDate), // patched below
      durationMinutes: 0,            // patched below
      stopsCount: 9,
    });
  }

  for (let d = 0; d < 5; d++) {
    const returnTripDate = new Date(tomorrow);
    returnTripDate.setDate(returnTripDate.getDate() + d);
    returnTripDate.setHours(20, 0, 0, 0);
    schedules.push({
      trainId: train.id,
      routeId: returnRoute!.id,
      originStationId: firstReturnStation!.id,
      destinationStationId: lastReturnStation!.id,
      departureAt: new Date(returnTripDate),
      arrivalAt: new Date(returnTripDate), // patched below
      durationMinutes: 0,            // patched below
      stopsCount: 9,
    });
  }

  // Load route stops for both routes upfront
  const routeStopsMap = new Map<string, { stationId: string; sequence: number; distanceKm: number }[]>();
  for (const r of [route!, returnRoute!]) {
    const stops = await prisma.routeStop.findMany({
      where: { routeId: r.id },
      orderBy: { sequence: 'asc' },
    });
    routeStopsMap.set(r.id, stops.map(s => ({ stationId: s.stationId, sequence: s.sequence, distanceKm: s.distanceKm! })));
  }

  // Compute duration from total route distance at 60 km/h
  function routeDuration(stops: { distanceKm: number }[]): number {
    const totalKm = stops[stops.length - 1].distanceKm - stops[0].distanceKm;
    return Math.ceil(totalKm / 60 * 60);
  }

  // Patch arrivalAt and durationMinutes using distance-based timing
  const patchedSchedules = schedules.map(s => {
    const stops = routeStopsMap.get(s.routeId!)!;
    const durationMinutes = routeDuration(stops);
    return { ...s, durationMinutes, arrivalAt: new Date(s.departureAt.getTime() + durationMinutes * 60_000) };
  });

  const createdSchedules = await Promise.all(
    patchedSchedules.map(s => prisma.trainSchedule.create({ data: s }))
  );

  // Create TripStopTimes using cumulative distanceKm at 60 km/h
  for (const schedule of createdSchedules) {
    const stops = routeStopsMap.get(schedule.routeId!)!;
    const originKm = stops[0].distanceKm;
    const stopTimes = stops.map(stop => {
      const minutesFromStart = Math.ceil((stop.distanceKm - originKm) / 60 * 60);
      const plannedDepartureAt = new Date(schedule.departureAt.getTime() + minutesFromStart * 60_000);
      const plannedArrivalAt = new Date(plannedDepartureAt.getTime() - 5 * 60_000); // 5 min dwell
      return {
        scheduleId: schedule.id,
        stationId: stop.stationId,
        sequence: stop.sequence,
        plannedArrivalAt,
        plannedDepartureAt,
      };
    });
    // First stop: arrival = departure (no dwell at origin)
    stopTimes[0].plannedArrivalAt = stopTimes[0].plannedDepartureAt;

    await Promise.all(stopTimes.map(st => prisma.tripStopTime.create({ data: st })));
  }

  const coachAssignments = [];
  const liveStatuses = [];

  for (const schedule of createdSchedules) {
    for (let p = 0; p < coaches.length; p++) {
      coachAssignments.push({
        scheduleId: schedule.id,
        coachId: coaches[p].id,
        positionNumber: p + 1,
      });
    }
    liveStatuses.push({
      scheduleId: schedule.id,
      state: 'scheduled',
      progressPercent: 0,
    });
  }

  await Promise.all([
    ...coachAssignments.map(ca => prisma.coachAssignment.create({ data: ca })),
    ...liveStatuses.map(ls => prisma.tripLiveStatus.create({ data: ls })),
  ]);

  console.log(`  ✅ Train with ${createdSchedules.length} upcoming trips created`);
}

async function seedFareRules() {
  console.log('\n💰 Seeding fare rules...');
  const route = await prisma.route.findUnique({ where: { code: 'Route-101' } });
  const seatClasses = await prisma.seatClass.findMany();
  const validFrom = new Date('2024-01-01');

  const fareRules = [];
  for (const sc of seatClasses) {
    fareRules.push({
      routeId: route!.id,
      seatClassId: sc.id,
      passengerCategory: 'ADULT' as const,
      baseFareMinor: sc.baseFareMinor,
      currency: 'ETB',
      validFrom,
    });
    fareRules.push({
      routeId: route!.id,
      seatClassId: sc.id,
      passengerCategory: 'CHILD' as const,
      baseFareMinor: Math.floor(sc.baseFareMinor * 0.5),
      discountPercent: 10,
      currency: 'ETB',
      validFrom,
    });
  }

  await Promise.all(
    fareRules.map(fr => prisma.routeFareRule.create({ data: fr }))
  );
  console.log(`  ✅ ${fareRules.length} fare rules for ADULT/CHILD categories created`);
}

async function seedCurrency() {
  console.log('\n💱 Seeding currency exchange rates...');
  const rates = [
    { from: 'ETB', to: 'DJF', rate: 3.25 },
    { from: 'ETB', to: 'USD', rate: 0.018 },
    { from: 'DJF', to: 'ETB', rate: 0.3077 },
    { from: 'USD', to: 'ETB', rate: 55.56 },
  ];

  for (const r of rates) {
    await prisma.currencyExchangeRate.upsert({
      where: {
        fromCurrency_toCurrency_effectiveDate: {
          fromCurrency: r.from as any,
          toCurrency: r.to as any,
          effectiveDate: new Date('2024-01-01'),
        },
      },
      update: { rate: r.rate },
      create: {
        fromCurrency: r.from as any,
        toCurrency: r.to as any,
        rate: r.rate,
        effectiveDate: new Date('2024-01-01'),
      },
    });
  }
  console.log(`  ✅ 4 currency exchange rates created`);
}

async function seedPaymentMethods() {
  console.log('\n💳 Seeding payment methods...');
  const methods = [
    { type: 'TELEBIRR', displayName: 'Telebirr', region: 'ETHIOPIA' },
    { type: 'CBE_BIRR', displayName: 'CBE Birr', region: 'ETHIOPIA' },
    { type: 'EBIRR', displayName: 'eBirr', region: 'ETHIOPIA' },
    { type: 'WAAFI', displayName: 'Waffi', region: 'DJIBOUTI' },
    { type: 'CARD', displayName: 'Credit/Debit Card', region: 'GLOBAL' },
    { type: 'WALLET', displayName: 'Wallet', region: 'GLOBAL' },
  ];

  for (const m of methods) {
    await prisma.paymentMethod.upsert({
      where: { type: m.type as any },
      update: {},
      create: { ...m, type: m.type as any, region: m.region as any },
    });
  }
  console.log(`  ✅ ${methods.length} payment methods created`);
}

async function seedSegmentFares() {
  console.log('\n📍 Seeding segment fare rules...');
  const route = await prisma.route.findUnique({
    where: { code: 'Route-101' },
    include: { stops: { orderBy: { sequence: 'asc' } } },
  });
  const seatClasses = await prisma.seatClass.findMany();
  const validFrom = new Date('2026-01-01');

  if (route && route.stops.length > 2) {
    for (const sc of seatClasses) {
      await prisma.segmentFareRule.create({
        data: {
          routeId: route.id,
          seatClassId: sc.id,
          originStopSequence: 1,
          destinationStopSequence: 3,
          baseFareMinor: Math.floor(sc.baseFareMinor * 0.4),
          validFrom,
        },
      }).catch(() => { });

      await prisma.segmentFareRule.create({
        data: {
          routeId: route.id,
          seatClassId: sc.id,
          originStopSequence: 5,
          destinationStopSequence: 9,
          baseFareMinor: Math.floor(sc.baseFareMinor * 0.6),
          validFrom,
        },
      }).catch(() => { });
    }
    console.log(`  ✅ ${seatClasses.length * 2} segment fare rules created`);
  }
}

async function seedNotificationTemplates() {
  console.log('\n🔔 Seeding notification templates...');
  // NOTE: `code` must match the templateKey passed by NotificationsService.send(...).
  // The event-driven handlers use the dotted event names (booking.created, payment.succeeded).
  const templates = [
    { id: uuidv4(), code: 'booking.created', channel: 'EMAIL', subject: 'Booking Confirmed', bodyTemplate: 'Your booking {{bookingRef}} is confirmed. Total: {{amount}} {{currency}}.' },
    { id: uuidv4(), code: 'payment.succeeded', channel: 'SMS', subject: 'Payment Received', bodyTemplate: 'Payment of {{amount}} {{currency}} received for booking {{bookingRef}}.' },
    { id: uuidv4(), code: 'payment.failed', channel: 'SMS', subject: 'Payment Failed', bodyTemplate: 'Payment for booking {{bookingRef}} could not be completed. Please try again.' },
    { id: uuidv4(), code: 'booking.cancelled', channel: 'EMAIL', subject: 'Booking Cancelled', bodyTemplate: 'Your booking {{bookingRef}} has been cancelled. Refund: {{refundAmount}} {{currency}}.' },
    // Templates below are not wired to handlers yet (Phase 2 — full event coverage).
    { id: uuidv4(), code: 'trip.departure', channel: 'PUSH', subject: 'Trip Departing Soon', bodyTemplate: 'Your trip {{route}} departs in {{minutes}} minutes' },
    { id: uuidv4(), code: 'trip.delay', channel: 'EMAIL', subject: 'Trip Delayed', bodyTemplate: 'Your trip {{route}} is delayed by {{delayMinutes}} minutes' },
    { id: uuidv4(), code: 'promotion.offer', channel: 'PUSH', subject: 'Special Offer', bodyTemplate: 'Get {{percentOff}}% off on {{route}}' },
  ];

  for (const t of templates) {
    await prisma.notificationTemplate.upsert({
      where: { code: t.code },
      // Refresh the editable fields on re-seed so template tweaks actually take effect.
      update: { channel: t.channel, subject: t.subject ?? null, bodyTemplate: t.bodyTemplate, active: true },
      create: t,
    });
  }
  console.log(`  ✅ ${templates.length} notification templates created`);
}

async function seedMenuAndFood() {
  console.log('\n🍽️ Seeding menu categories and items...');
  const beverages = await prisma.menuCategory.upsert({
    where: { id: uuidv4() },
    update: {},
    create: { id: uuidv4(), name: 'Beverages' },
  });
  const snacks = await prisma.menuCategory.upsert({
    where: { id: uuidv4() },
    update: {},
    create: { id: uuidv4(), name: 'Snacks' },
  });

  const schedule = await prisma.trainSchedule.findFirst();
  if (schedule) {
    const coffeeId = uuidv4();
    const juiceId = uuidv4();
    const sandwichId = uuidv4();

    await prisma.menuItem.create({
      data: { id: coffeeId, scheduleId: schedule.id, categoryId: beverages.id, name: 'Ethiopian Coffee', priceMinor: 50 },
    }).catch(() => { }); // ignore if exists
    await prisma.menuItem.create({
      data: { id: juiceId, scheduleId: schedule.id, categoryId: beverages.id, name: 'Fresh Juice', priceMinor: 35 },
    }).catch(() => { }); // ignore if exists
    await prisma.menuItem.create({
      data: { id: sandwichId, scheduleId: schedule.id, categoryId: snacks.id, name: 'Sandwich', priceMinor: 80 },
    }).catch(() => { }); // ignore if exists
  }
  console.log(`  ✅ Menu categories and items created`);
}

async function seedPromotions() {
  console.log('\n🎉 Seeding promotions...');
  const promos = [
    { id: uuidv4(), title: 'Early Bird Discount', code: 'EARLY20', percentOff: 20, validUntil: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000) },
    { id: uuidv4(), title: 'Student Discount', code: 'STUDENT15', percentOff: 15, validUntil: new Date(Date.now() + 60 * 24 * 60 * 60 * 1000) },
    { id: uuidv4(), title: 'Group Booking', code: 'GROUP10', amountOffMinor: 100, validUntil: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000) },
  ];

  for (const p of promos) {
    await prisma.promotion.upsert({
      where: { code: p.code },
      update: {},
      create: p,
    });
  }
  console.log(`  ✅ ${promos.length} promotions created`);
}

async function seedFAQ() {
  console.log('\n❓ Seeding FAQ...');
  const generalId = uuidv4();
  const bookingId = uuidv4();
  const general = await prisma.faqCategory.upsert({
    where: { id: generalId },
    update: {},
    create: { id: generalId, title: 'General', iconKey: 'help' },
  });
  const booking = await prisma.faqCategory.upsert({
    where: { id: bookingId },
    update: {},
    create: { id: bookingId, title: 'Booking', iconKey: 'book' },
  });

  await prisma.faqArticle.upsert({
    where: { id: uuidv4() },
    update: {},
    create: { id: uuidv4(), categoryId: general.id, question: 'What is EDR?', answerMarkdown: 'Ethio-Djibouti Railway' },
  });
  await prisma.faqArticle.upsert({
    where: { id: uuidv4() },
    update: {},
    create: { id: uuidv4(), categoryId: booking.id, question: 'How to book?', answerMarkdown: 'Use the booking system' },
  });
  console.log(`  ✅ FAQ categories and articles created`);
}

async function seedFraudRules() {
  console.log('\n🔐 Seeding fraud detection rules...');
  const rules = [
    { type: 'RAPID_BOOKINGS', threshold: 10, enabled: true },
    { type: 'HIGH_VALUE_BOOKING', threshold: 500000, enabled: true },
    { type: 'UNUSUAL_DEVICE', threshold: 0.8, enabled: true },
  ];

  for (const r of rules) {
    await prisma.fraudRule.upsert({
      where: { type: r.type },
      update: {},
      create: r,
    });
  }
  console.log(`  ✅ ${rules.length} fraud detection rules created`);
}

// Run a seed step in isolation: if it throws (FK conflict, duplicate row,
// missing record, etc.) log the error and keep going so the rest of the seed —
// and the API startup that follows it — are never blocked by one bad step.
async function runStep(name: string, step: () => Promise<unknown>): Promise<boolean> {
  try {
    await step();
    return true;
  } catch (e) {
    console.error(`⚠️  Seed step "${name}" failed — skipping and continuing:`, e);
    return false;
  }
}

async function main() {
  console.log('🌱 Comprehensive EDR Seed Starting...\n');

  const steps: Array<[string, () => Promise<unknown>]> = [
    ['system users', seedSystemUsers],
    ['fare rules', seedFareRules],
    ['segment fares', seedSegmentFares],
    ['currency', seedCurrency],
    ['notification templates', seedNotificationTemplates]
  ];

  let failed = 0;
  for (const [name, step] of steps) {
    if (!(await runStep(name, step))) failed++;
  }

  if (failed > 0) {
    console.warn(`\n⚠️  Seed finished with ${failed}/${steps.length} step(s) failed (see logs above).\n`);
  } else {
    console.log('\n✅ Seed complete!\n');
  }
  console.log('🔑 System Users:');
  console.log('  Admin:       admin@edr-platform.com / admin123');
  console.log('  Passenger:   kelemu@email.com / password123');
  console.log('  Agent:       agent@edr-platform.com / agent123');
  console.log('  Supervisor:  supervisor@edr-platform.com / supervisor123');
  console.log('  Staff:       staff@edr-platform.com / staff123');
}

main()
  .catch((e) => {
    // Intentionally do NOT process.exit(1): the docker entrypoint runs under
    // `set -e`, so a non-zero exit here would abort container startup and the
    // API would never boot. Log and exit cleanly instead.
    console.error('❌ Seed crashed unexpectedly — continuing so the API can start:', e);
  })
  .finally(async () => {
    await prisma.$disconnect();
  });
