/**
 * SEGMENT-BASED SEAT RESERVATION EXAMPLE
 *
 * Demonstrates the complete flow for booking Addis Ababa → Dire Dawa
 * on the Addis Ababa → Djibouti route with segment-based seat management.
 *
 * Route: Addis Ababa (seq:1) → Adama (seq:2) → Awash (seq:3) → Dire Dawa (seq:4) → Aysha (seq:5) → Djibouti (seq:6)
 * Booking: Addis Ababa → Dire Dawa (segments: 1→2, 2→3, 3→4)
 */

import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

async function exampleBookingFlow() {
  console.log('=== SEGMENT-BASED BOOKING FLOW ===\n');

  const scheduleId = 'schedule_add_dji_001';
  const passengerId = 'passenger_kelemu';
  const seatIds = ['seat_coach_a_1a', 'seat_coach_a_1b'];
  const originStationId = 'st_ADD';
  const destinationStationId = 'st_DRE';

  try {
    console.log('1. Checking seat availability...');
    const segments = await getJourneySegments(scheduleId, originStationId, destinationStationId);
    console.log('Journey segments:', segments.map(s => `${s.fromName} → ${s.toName}`));

    console.log('\n2. Holding seats...');
    const holdResult = await holdSeatsTransaction(scheduleId, seatIds, passengerId, originStationId, destinationStationId);
    console.log('Hold created:', holdResult);

    console.log('\n3. Processing payment...');
    await new Promise(resolve => setTimeout(resolve, 5000));

    console.log('\n4. Confirming booking...');
    const bookingId = 'booking_' + Date.now();
    const confirmResult = await confirmBookingTransaction(holdResult.holdId, bookingId, segments);
    console.log('Booking confirmed:', confirmResult);

    console.log('\n5. Simulating trip progress...');
    await simulateTripProgress(scheduleId, segments);

  } catch (error) {
    console.error('Booking flow error:', error);
  }
}

async function getJourneySegments(scheduleId: string, originStationId: string, destinationStationId: string) {
  const stopTimes = await prisma.tripStopTime.findMany({
    where: { scheduleId },
    include: { station: true },
    orderBy: { sequence: 'asc' },
  });

  const originStop = stopTimes.find(st => st.stationId === originStationId);
  const destinationStop = stopTimes.find(st => st.stationId === destinationStationId);

  if (!originStop || !destinationStop || originStop.sequence >= destinationStop.sequence) {
    throw new Error('Invalid origin/destination');
  }

  const segments = [];
  for (let i = originStop.sequence; i < destinationStop.sequence; i++) {
    const fromStop = stopTimes.find(st => st.sequence === i);
    const toStop = stopTimes.find(st => st.sequence === i + 1);
    if (fromStop && toStop) {
      segments.push({
        fromStationId: fromStop.stationId,
        toStationId: toStop.stationId,
        fromSequence: fromStop.sequence,
        toSequence: toStop.sequence,
        fromName: fromStop.station.name,
        toName: toStop.station.name,
      });
    }
  }
  return segments;
}

async function holdSeatsTransaction(scheduleId: string, seatIds: string[], passengerId: string, originStationId: string, destinationStationId: string) {
  return prisma.$transaction(async (tx) => {
    console.log('  → Starting seat hold transaction...');

    const seats = await tx.seat.findMany({ where: { id: { in: seatIds } }, include: { coach: true } });
    if (seats.length !== seatIds.length) throw new Error('Some seats not found');

    for (const seat of seats) {
      if (seat.status !== 'AVAILABLE') {
        throw new Error(`Seat ${seat.seatNumber} is not available (status: ${seat.status})`);
      }
    }

    const expiresAt = new Date(Date.now() + 10 * 60 * 1000);
    const seatHold = await tx.seatHold.create({
      data: { scheduleId, seatIds, passengerId, expiresAt },
    });

    await tx.seat.updateMany({ where: { id: { in: seatIds } }, data: { status: 'HELD', heldUntil: expiresAt } });

    console.log('  → Seats held successfully');
    return { holdId: seatHold.id, expiresAt, seats: seatIds.length };
  });
}

async function confirmBookingTransaction(holdId: string, bookingId: string, segments: any[]) {
  return prisma.$transaction(async (tx) => {
    console.log('  → Starting booking confirmation transaction...');

    const hold = await tx.seatHold.findUnique({ where: { id: holdId } });
    if (!hold || hold.expiresAt < new Date()) throw new Error('Hold expired or not found');

    const booking = await tx.booking.create({
      data: {
        id: bookingId,
        bookingRef: 'BK' + Date.now().toString().slice(-6),
        passengerId: hold.passengerId,
        scheduleId: hold.scheduleId,
        status: 'CONFIRMED',
        totalMinor: 45000,
        currency: 'ETB',
      },
    });

    const journey = await tx.journey.create({
      data: { passengerId: hold.passengerId, status: 'CONFIRMED', totalMinor: 45000, currency: 'ETB' },
    });

    for (const seatId of hold.seatIds) {
      for (let i = 0; i < segments.length; i++) {
        await tx.journeySegment.create({
          data: {
            journeyId: journey.id,
            scheduleId: hold.scheduleId,
            segmentOrder: i + 1,
            seatId,
            departureStationId: segments[i].fromStationId,
            arrivalStationId: segments[i].toStationId,
          },
        });
      }
    }

    for (const seatId of hold.seatIds) {
      await tx.bookingSeat.create({ data: { bookingId, seatId, passengerName: 'Kelemu Ketsela' } });
    }

    await tx.seat.updateMany({ where: { id: { in: hold.seatIds } }, data: { status: 'BOOKED', heldUntil: null } });
    await tx.seatHold.delete({ where: { id: holdId } });

    console.log('  → Booking confirmed successfully');
    return { bookingId, bookingRef: booking.bookingRef, confirmedSeats: hold.seatIds.length, segments: segments.length };
  });
}

async function simulateTripProgress(scheduleId: string, bookedSegments: any[]) {
  console.log('  → Simulating trip progress...');

  for (const segment of bookedSegments) {
    console.log(`  → Train approaching ${segment.toName}...`);

    await prisma.tripLiveStatus.upsert({
      where: { scheduleId },
      update: { currentLocationLabel: segment.toName, progressPercent: Math.round((segment.toSequence / 4) * 100) },
      create: {
        scheduleId,
        state: 'EN_ROUTE',
        currentLocationLabel: segment.toName,
        progressPercent: Math.round((segment.toSequence / 4) * 100),
        delayMinutes: 0,
      },
    });

    if (segment.toName === 'Dire Dawa') {
      console.log('  → Passengers reached destination, releasing seats...');
      await releaseSeatsAtStation(scheduleId, segment.toStationId);
    }

    await new Promise(resolve => setTimeout(resolve, 2000));
  }
}

async function releaseSeatsAtStation(scheduleId: string, stationId: string) {
  return prisma.$transaction(async (tx) => {
    const completedSegments = await tx.journeySegment.findMany({
      where: { scheduleId, arrivalStationId: stationId },
      include: { journey: { include: { journeySegments: { where: { scheduleId } } } } },
    });

    const seatsToRelease: string[] = [];

    for (const segment of completedSegments) {
      const passengerSegments = segment.journey.journeySegments.filter((js: any) => js.seatId === segment.seatId);
      const maxOrder = Math.max(...passengerSegments.map((js: any) => js.segmentOrder));
      if (segment.segmentOrder === maxOrder) seatsToRelease.push(segment.seatId!);
    }

    if (seatsToRelease.length > 0) {
      await tx.seat.updateMany({ where: { id: { in: seatsToRelease } }, data: { status: 'AVAILABLE' } });
      console.log(`  → Released ${seatsToRelease.length} seats at station`);
    }

    return seatsToRelease;
  });
}

async function checkOverlappingReservations(tx: any, scheduleId: string, seatId: string, segments: any[]) {
  const activeHolds = await tx.seatHold.findMany({
    where: { scheduleId, seatIds: { has: seatId }, expiresAt: { gt: new Date() } },
  });

  const activeBookings = await tx.journeySegment.findMany({
    where: {
      scheduleId,
      seatId,
      journey: { status: { in: ['PENDING_PAYMENT', 'CONFIRMED'] } },
    },
  });

  return [...activeHolds, ...activeBookings];
}

if (require.main === module) {
  exampleBookingFlow()
    .then(() => console.log('\n=== EXAMPLES COMPLETED ==='))
    .catch(console.error)
    .finally(() => prisma.$disconnect());
}

export {
  exampleBookingFlow,
  getJourneySegments,
  holdSeatsTransaction,
  confirmBookingTransaction,
  simulateTripProgress,
  releaseSeatsAtStation,
  checkOverlappingReservations,
};
