import { Injectable, BadRequestException, NotFoundException } from '@nestjs/common';
import { PrismaService } from '../../common/prisma.service';
import { SeatsService } from '../seats/seats.service';
import { VerifaydaService } from '../verifayda/verifayda.service';
import { CurrencyService } from '../currency/currency.service';
import { FareEngineService } from '../fare-engine/fare-engine.service';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { CreateGuestBookingDto, SavedPassengerProfileDto } from './guest-booking.dto';
import { Currency, PassengerCategory, IdDocumentType } from '@prisma/client';
import * as bcrypt from 'bcrypt';

function generateRef(): string {
  const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
  return 'EDR-' + Array.from({ length: 6 }, () => chars[Math.floor(Math.random() * chars.length)]).join('');
}

function calculateAge(dateOfBirth: Date): number {
  const today = new Date();
  let age = today.getFullYear() - dateOfBirth.getFullYear();
  const monthDiff = today.getMonth() - dateOfBirth.getMonth();
  if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < dateOfBirth.getDate())) age--;
  return age;
}

@Injectable()
export class GuestBookingService {
  constructor(
    private prisma: PrismaService,
    private seatsService: SeatsService,
    private verifaydaService: VerifaydaService,
    private currencyService: CurrencyService,
    private fareEngine: FareEngineService,
    private eventEmitter: EventEmitter2,
  ) {}

  async createGuestBooking(dto: CreateGuestBookingDto) {
    if (dto.bookingType === 'ROUND_TRIP')         return this.createGuestRoundTripBooking(dto);
    if (dto.bookingType === 'TRANSIT')            return this.createGuestTransitBooking(dto);
    if (dto.bookingType === 'ROUND_TRIP_TRANSIT') return this.createGuestRoundTripTransitBooking(dto);
    return this.createGuestOneWayBooking(dto);
  }

  private async createGuestOneWayBooking(dto: CreateGuestBookingDto) {
    // Validate hold
    const hold = await this.prisma.seatHold.findUnique({ where: { id: dto.holdId } });
    if (!hold || hold.expiresAt < new Date()) {
      throw new BadRequestException('Seat hold expired or not found');
    }

    // Get schedule
    const schedule = await this.prisma.trainSchedule.findUnique({
      where: { id: dto.scheduleId },
      include: {
        originStation: true,
        destinationStation: true,
        stopTimes: { include: { station: true }, orderBy: { sequence: 'asc' } },
      },
    });
    if (!schedule) throw new NotFoundException('Schedule not found');

    const originStop = schedule.stopTimes.find(s => s.stationId === dto.originStationId);
    const destStop = schedule.stopTimes.find(s => s.stationId === dto.destinationStationId);
    if (!originStop || !destStop) throw new NotFoundException('Origin or destination not found');

    const segmentRoute = `${originStop.station.code}-${destStop.station.code}`;
    const fullRoute = `${schedule.originStation.code}-${schedule.destinationStation.code}`;

    // Process passengers with Verifayda verification
    const passengersData = [];
    let adultCount = 0, childCount = 0;

    for (const passenger of dto.passengers) {
      const dateOfBirth = new Date(passenger.dateOfBirth);
      const age = calculateAge(dateOfBirth);
      const category: PassengerCategory = age < 5 ? PassengerCategory.CHILD : PassengerCategory.ADULT;
      if (category === PassengerCategory.ADULT) adultCount++; else childCount++;

      let passengerName = passenger.passengerName;
      let verifaydaVerified = false;
      let verifaydaData: Record<string, any> | undefined;
      let nationality = passenger.nationality;

      // Determine if passenger is Ethiopian
      const isEthiopian = passenger.nationality === 'Ethiopian' || 
                          passenger.nationality === 'ETHIOPIAN' ||
                          passenger.idDocumentType === IdDocumentType.NATIONAL_ID;
      
      // Ethiopian with National ID
      if (isEthiopian && passenger.idDocumentType === IdDocumentType.NATIONAL_ID) {
        if (passenger.idDocumentNumber) {
          // Attempt Fayda verification
          const verification = await this.verifaydaService.verifyNationalId(passenger.idDocumentNumber);
          if (!verification.verified) {
            throw new BadRequestException(
              `Verifayda verification failed for ${passenger.passengerName}: ${verification.failureReason}`
            );
          }
          passengerName = verification.passengerData?.fullName || passengerName;
          verifaydaVerified = true;
          verifaydaData = verification.passengerData?.profileData;
        }
        nationality = 'Ethiopian';
      } 
      // International passenger with Passport (non-Ethiopian)
      else if (!isEthiopian && passenger.idDocumentType === IdDocumentType.PASSPORT) {
        // Passport details are required for international passengers
        if (!passenger.passportNumber || !passenger.passportCountry) {
          throw new BadRequestException(`Passport number and country required for ${passenger.passengerName}`);
        }
        nationality = nationality || (passenger.passportCountry === 'Djibouti' ? 'Djiboutian' : 'Other');
      }
      // Ethiopian with Passport (manual entry without Fayda)
      else if (isEthiopian && passenger.idDocumentType === IdDocumentType.PASSPORT) {
        // Ethiopians can use passport instead of national ID
        nationality = 'Ethiopian';
      }
      // International with National ID (e.g., Djiboutian national ID)
      else if (!isEthiopian && passenger.idDocumentType === IdDocumentType.NATIONAL_ID) {
        nationality = nationality || 'Other';
      }

      passengersData.push({
        ...passenger,
        passengerName,
        dateOfBirth,
        category,
        verifaydaVerified,
        verifaydaData,
        nationality,
      });
    }

    // Calculate fare
    const primaryNationality = passengersData[0]?.nationality;
    const baseFareMinor = await this.getBaseFare(
      dto.scheduleId,
      dto.seatClassId,
      segmentRoute,
      fullRoute,
      primaryNationality
    );

    const adultFareMinor = baseFareMinor * adultCount;
    const paidChildrenCount = Math.max(0, childCount - 1);
    const childFareMinor = baseFareMinor * paidChildrenCount;
    const totalBaseFareMinor = adultFareMinor + childFareMinor;

    let discountMinor = 0;
    if (dto.promoCode) {
      const promo = await this.prisma.promotion.findUnique({ where: { code: dto.promoCode } });
      if (promo?.active && promo.validUntil > new Date()) {
        discountMinor = promo.percentOff
          ? Math.round(totalBaseFareMinor * promo.percentOff / 100)
          : (promo.amountOffMinor ?? 0);
      }
    }

    const taxesMinor = Math.round(totalBaseFareMinor * 0.05);
    const totalMinor = Math.max(0, totalBaseFareMinor - discountMinor + taxesMinor);

    const displayCurrency = dto.displayCurrency || Currency.ETB;
    let displayTotalMinor = totalMinor;
    if (displayCurrency !== Currency.ETB) {
      displayTotalMinor = await this.currencyService.convertAmount(totalMinor, Currency.ETB, displayCurrency);
    }

    // Create or get guest passenger
    const firstPassenger = passengersData[0];
    const { guestPassenger, userId, createdAccount } = await this.resolveGuestPassenger(dto, firstPassenger);

    // Save passenger details for future use (if requested)
    if (dto.savePassengerDetails && (dto.createAccount || dto.deviceId)) {
      for (const passenger of passengersData) {
        // Note: SavedPassengerProfile will be available after migration
        // Temporarily disabled until prisma generate completes
        // await this.prisma.savedPassengerProfile.create({ ... });
      }
    }

    // Create booking
    const booking = await this.prisma.booking.create({
      data: {
        bookingRef: generateRef(),
        passengerId: guestPassenger.id,
        scheduleId: dto.scheduleId,
        status: 'PENDING_PAYMENT',
        totalMinor,
        adultCount,
        childCount,
        displayCurrency,
        displayTotalMinor,
        bookingType: 'ONE_WAY',
        userAgent:    dto.deviceId,
        contactEmail: firstPassenger.email || null,
        contactPhone: firstPassenger.phone || null,
        seats: {
          create: passengersData.map((p) => ({
            seat: { connect: { id: p.seatId } },
            passengerName: p.passengerName,
            dateOfBirth: p.dateOfBirth,
            passengerCategory: p.category,
            idDocumentType: p.idDocumentType,
            passportNumber: p.passportNumber,
            passportCountry: p.passportCountry,
            verifaydaVerified: p.verifaydaVerified,
            verifaydaData: p.verifaydaData || undefined,
            fareMinor: p.category === PassengerCategory.ADULT ? baseFareMinor : (paidChildrenCount > 0 ? baseFareMinor : 0),
            displayCurrency,
          })),
        },
      },
      include: {
        seats: { include: { seat: { include: { coach: true } } } },
        schedule: { include: { originStation: true, destinationStation: true, train: true } },
      },
    });

    // Confirm seats
    await this.seatsService.confirmSeats(dto.passengers.map(p => p.seatId));
    this.eventEmitter.emit('booking.created', { booking });

    return {
      ...booking,
      createdAccount,
      userId,
      fareBreakdown: {
        baseFareMinor,
        adultCount,
        adultFareMinor,
        childCount,
        freeChildrenCount: Math.min(childCount, 1),
        paidChildrenCount,
        childFareMinor,
        totalBaseFareMinor,
        discountMinor,
        taxesFeesMinor: taxesMinor,
        totalMinor,
        currency: 'ETB',
        displayCurrency,
        displayTotalMinor,
      },
    };
  }

  private async createGuestRoundTripBooking(dto: CreateGuestBookingDto) {
    if (!dto.returnScheduleId || !dto.returnHoldId || !dto.returnOriginStationId || !dto.returnDestinationStationId) {
      throw new BadRequestException('returnScheduleId, returnHoldId, returnOriginStationId and returnDestinationStationId are required for ROUND_TRIP');
    }

    // Validate both holds
    const [outboundHold, returnHold] = await Promise.all([
      this.prisma.seatHold.findUnique({ where: { id: dto.holdId } }),
      this.prisma.seatHold.findUnique({ where: { id: dto.returnHoldId } }),
    ]);
    if (!outboundHold || outboundHold.expiresAt < new Date()) throw new BadRequestException('Outbound seat hold expired or not found');
    if (!returnHold   || returnHold.expiresAt   < new Date()) throw new BadRequestException('Return seat hold expired or not found');

    // Validate passengers have returnSeatId
    for (const p of dto.passengers) {
      if (!p.returnSeatId) throw new BadRequestException(`returnSeatId is required for each passenger in a ROUND_TRIP booking (missing for ${p.passengerName})`);
    }

    // Load both schedules
    const [outboundSchedule, returnSchedule] = await Promise.all([
      this.prisma.trainSchedule.findUnique({
        where: { id: dto.scheduleId },
        include: { originStation: true, destinationStation: true, stopTimes: { include: { station: true }, orderBy: { sequence: 'asc' } } },
      }),
      this.prisma.trainSchedule.findUnique({
        where: { id: dto.returnScheduleId },
        include: { originStation: true, destinationStation: true, stopTimes: { include: { station: true }, orderBy: { sequence: 'asc' } } },
      }),
    ]);
    if (!outboundSchedule) throw new NotFoundException('Outbound schedule not found');
    if (!returnSchedule)   throw new NotFoundException('Return schedule not found');

    const outboundOriginStop = outboundSchedule.stopTimes.find(s => s.stationId === dto.originStationId);
    const outboundDestStop   = outboundSchedule.stopTimes.find(s => s.stationId === dto.destinationStationId);
    const returnOriginStop   = returnSchedule.stopTimes.find(s => s.stationId === dto.returnOriginStationId);
    const returnDestStop     = returnSchedule.stopTimes.find(s => s.stationId === dto.returnDestinationStationId);
    if (!outboundOriginStop || !outboundDestStop) throw new NotFoundException('Outbound origin or destination not found on schedule');
    if (!returnOriginStop   || !returnDestStop)   throw new NotFoundException('Return origin or destination not found on schedule');

    const outboundSegmentRoute = `${outboundOriginStop.station.code}-${outboundDestStop.station.code}`;
    const outboundFullRoute    = `${outboundSchedule.originStation.code}-${outboundSchedule.destinationStation.code}`;
    const returnSegmentRoute   = `${returnOriginStop.station.code}-${returnDestStop.station.code}`;
    const returnFullRoute      = `${returnSchedule.originStation.code}-${returnSchedule.destinationStation.code}`;

    // Process passengers (verify identity once — same person travels both legs)
    const passengersData: any[] = [];
    let adultCount = 0, childCount = 0;

    for (const passenger of dto.passengers) {
      const dateOfBirth = new Date(passenger.dateOfBirth);
      const age = calculateAge(dateOfBirth);
      const category: PassengerCategory = age < 5 ? PassengerCategory.CHILD : PassengerCategory.ADULT;
      if (category === PassengerCategory.ADULT) adultCount++; else childCount++;

      let passengerName = passenger.passengerName;
      let verifaydaVerified = false;
      let verifaydaData: Record<string, any> | undefined;
      let nationality = passenger.nationality;

      const isEthiopian = passenger.nationality === 'Ethiopian' ||
                          passenger.nationality === 'ETHIOPIAN' ||
                          passenger.idDocumentType === IdDocumentType.NATIONAL_ID;

      if (isEthiopian && passenger.idDocumentType === IdDocumentType.NATIONAL_ID) {
        if (passenger.idDocumentNumber) {
          const verification = await this.verifaydaService.verifyNationalId(passenger.idDocumentNumber);
          if (!verification.verified) throw new BadRequestException(`Verifayda verification failed for ${passenger.passengerName}: ${verification.failureReason}`);
          passengerName     = verification.passengerData?.fullName || passengerName;
          verifaydaVerified = true;
          verifaydaData     = verification.passengerData?.profileData;
        }
        nationality = 'Ethiopian';
      } else if (!isEthiopian && passenger.idDocumentType === IdDocumentType.PASSPORT) {
        if (!passenger.passportNumber || !passenger.passportCountry) throw new BadRequestException(`Passport number and country required for ${passenger.passengerName}`);
        nationality = nationality || (passenger.passportCountry === 'Djibouti' ? 'Djiboutian' : 'Other');
      } else if (isEthiopian && passenger.idDocumentType === IdDocumentType.PASSPORT) {
        nationality = 'Ethiopian';
      } else {
        nationality = nationality || 'Other';
      }

      passengersData.push({ ...passenger, passengerName, dateOfBirth, category, verifaydaVerified, verifaydaData, nationality });
    }

    // Calculate fares for both legs
    const returnSeatClassId = dto.returnSeatClassId || dto.seatClassId;
    const primaryNationality = passengersData[0]?.nationality;

    const [outboundBaseFare, returnBaseFare] = await Promise.all([
      this.getBaseFare(dto.scheduleId, dto.seatClassId, outboundSegmentRoute, outboundFullRoute, primaryNationality),
      this.getBaseFare(dto.returnScheduleId, returnSeatClassId, returnSegmentRoute, returnFullRoute, primaryNationality),
    ]);

    const paidChildrenCount      = Math.max(0, childCount - 1);
    const outboundTotalBase      = outboundBaseFare * adultCount + outboundBaseFare * paidChildrenCount;
    const returnTotalBase        = returnBaseFare  * adultCount + returnBaseFare  * paidChildrenCount;
    const combinedBaseFareMinor  = outboundTotalBase + returnTotalBase;

    let discountMinor = 0;
    if (dto.promoCode) {
      const promo = await this.prisma.promotion.findUnique({ where: { code: dto.promoCode } });
      if (promo?.active && promo.validUntil > new Date()) {
        discountMinor = promo.percentOff
          ? Math.round(combinedBaseFareMinor * promo.percentOff / 100)
          : (promo.amountOffMinor ?? 0);
      }
    }

    const taxesMinor = Math.round(combinedBaseFareMinor * 0.05);
    const totalMinor = Math.max(0, combinedBaseFareMinor - discountMinor + taxesMinor);

    const displayCurrency = dto.displayCurrency || Currency.ETB;
    const displayTotalMinor = displayCurrency !== Currency.ETB
      ? await this.currencyService.convertAmount(totalMinor, Currency.ETB, displayCurrency)
      : totalMinor;

    // Create or resolve guest passenger (same as one-way)
    const { guestPassenger, userId, createdAccount } = await this.resolveGuestPassenger(dto, passengersData[0]);

    // Create booking with outbound seats; return seats confirmed separately
    const outboundSeatIds = dto.passengers.map(p => p.seatId);
    const returnSeatIds   = dto.passengers.map(p => p.returnSeatId!);

    const booking = await this.prisma.booking.create({
      data: {
        bookingRef:                  generateRef(),
        passengerId:                 guestPassenger.id,
        scheduleId:                  dto.scheduleId,
        status:                      'PENDING_PAYMENT',
        bookingType:                 'ROUND_TRIP',
        totalMinor,
        adultCount,
        childCount,
        displayCurrency,
        displayTotalMinor,
        returnScheduleId:            dto.returnScheduleId,
        returnOriginStationId:       dto.returnOriginStationId,
        returnDestinationStationId:  dto.returnDestinationStationId,
        returnHoldId:                dto.returnHoldId,
        returnSeatClassId,
        returnLegStatus:             'NEITHER_USED',
        userAgent:                   dto.deviceId,
        contactEmail:                passengersData[0]?.email || null,
        contactPhone:                passengersData[0]?.phone || null,
        seats: {
          create: [
            ...passengersData.map((p) => ({
              seat:              { connect: { id: p.seatId } },
              leg:               1,
              scheduleId:        dto.scheduleId,
              passengerName:     p.passengerName,
              dateOfBirth:       p.dateOfBirth,
              passengerCategory: p.category,
              idDocumentType:    p.idDocumentType,
              passportNumber:    p.passportNumber,
              passportCountry:   p.passportCountry,
              verifaydaVerified: p.verifaydaVerified,
              verifaydaData:     p.verifaydaData || undefined,
              fareMinor:         p.category === PassengerCategory.ADULT ? outboundBaseFare : (paidChildrenCount > 0 ? outboundBaseFare : 0),
              displayCurrency,
            })),
            ...passengersData.map((p) => ({
              seat:              { connect: { id: p.returnSeatId } },
              leg:               2,
              scheduleId:        dto.returnScheduleId,
              passengerName:     p.passengerName,
              dateOfBirth:       p.dateOfBirth,
              passengerCategory: p.category,
              idDocumentType:    p.idDocumentType,
              passportNumber:    p.passportNumber,
              passportCountry:   p.passportCountry,
              verifaydaVerified: p.verifaydaVerified,
              verifaydaData:     p.verifaydaData || undefined,
              fareMinor:         p.category === PassengerCategory.ADULT ? returnBaseFare : (paidChildrenCount > 0 ? returnBaseFare : 0),
              displayCurrency,
            })),
          ],
        },
      } as any,
      include: {
        seats:    { include: { seat: { include: { coach: true } } } },
        schedule: { include: { originStation: true, destinationStation: true, train: true } },
      },
    });

    await Promise.all([
      this.seatsService.confirmSeats(outboundSeatIds),
      this.seatsService.confirmSeats(returnSeatIds),
    ]);
    this.eventEmitter.emit('booking.created', { booking });

    return {
      ...booking,
      createdAccount,
      userId,
      fareBreakdown: {
        outboundBaseFareMinor: outboundBaseFare,
        returnBaseFareMinor:   returnBaseFare,
        adultCount,
        childCount,
        freeChildrenCount:     Math.min(childCount, 1),
        paidChildrenCount,
        combinedBaseFareMinor,
        discountMinor,
        taxesFeesMinor:        taxesMinor,
        totalMinor,
        currency:              'ETB',
        displayCurrency,
        displayTotalMinor,
      },
    };
  }

  private async createGuestTransitBooking(dto: CreateGuestBookingDto) {
    if (!dto.leg2ScheduleId || !dto.leg2HoldId || !dto.transitStationId || !dto.leg2DestinationStationId) {
      throw new BadRequestException('leg2ScheduleId, leg2HoldId, transitStationId and leg2DestinationStationId are required for TRANSIT bookings');
    }

    const [leg1Hold, leg2Hold] = await Promise.all([
      this.prisma.seatHold.findUnique({ where: { id: dto.holdId } }),
      this.prisma.seatHold.findUnique({ where: { id: dto.leg2HoldId } }),
    ]);
    if (!leg1Hold || leg1Hold.expiresAt < new Date()) throw new BadRequestException('Leg-1 seat hold expired or not found');
    if (!leg2Hold || leg2Hold.expiresAt < new Date()) throw new BadRequestException('Leg-2 seat hold expired or not found');

    for (const p of dto.passengers) {
      if (!p.leg2SeatId) throw new BadRequestException(`leg2SeatId is required for each passenger in a TRANSIT booking (missing for ${p.passengerName})`);
    }

    const [leg1Schedule, leg2Schedule] = await Promise.all([
      this.prisma.trainSchedule.findUnique({
        where: { id: dto.scheduleId },
        include: { originStation: true, destinationStation: true, stopTimes: { include: { station: true }, orderBy: { sequence: 'asc' } } },
      }),
      this.prisma.trainSchedule.findUnique({
        where: { id: dto.leg2ScheduleId },
        include: { originStation: true, destinationStation: true, stopTimes: { include: { station: true }, orderBy: { sequence: 'asc' } } },
      }),
    ]);
    if (!leg1Schedule) throw new NotFoundException('Leg-1 schedule not found');
    if (!leg2Schedule) throw new NotFoundException('Leg-2 schedule not found');

    const leg1OriginStop = leg1Schedule.stopTimes.find(s => s.stationId === dto.originStationId);
    const leg1DestStop   = leg1Schedule.stopTimes.find(s => s.stationId === dto.transitStationId);
    const leg2OriginStop = leg2Schedule.stopTimes.find(s => s.stationId === dto.transitStationId);
    const leg2DestStop   = leg2Schedule.stopTimes.find(s => s.stationId === dto.leg2DestinationStationId);
    if (!leg1OriginStop || !leg1DestStop) throw new NotFoundException('Leg-1 origin or transit station not found on schedule');
    if (!leg2OriginStop || !leg2DestStop) throw new NotFoundException('Transit or leg-2 destination not found on leg-2 schedule');

    // Process passengers (verify identity once)
    const passengersData: any[] = [];
    let adultCount = 0, childCount = 0;
    for (const passenger of dto.passengers) {
      const dateOfBirth = new Date(passenger.dateOfBirth);
      const age = calculateAge(dateOfBirth);
      const category: PassengerCategory = age < 5 ? PassengerCategory.CHILD : PassengerCategory.ADULT;
      if (category === PassengerCategory.ADULT) adultCount++; else childCount++;

      let passengerName = passenger.passengerName;
      let verifaydaVerified = false;
      let verifaydaData: Record<string, any> | undefined;
      let nationality = passenger.nationality;

      const isEthiopian = passenger.nationality === 'Ethiopian' || passenger.nationality === 'ETHIOPIAN' || passenger.idDocumentType === IdDocumentType.NATIONAL_ID;
      if (isEthiopian && passenger.idDocumentType === IdDocumentType.NATIONAL_ID && passenger.idDocumentNumber) {
        const verification = await this.verifaydaService.verifyNationalId(passenger.idDocumentNumber);
        if (!verification.verified) throw new BadRequestException(`Verifayda verification failed for ${passenger.passengerName}: ${verification.failureReason}`);
        passengerName = verification.passengerData?.fullName || passengerName;
        verifaydaVerified = true;
        verifaydaData = verification.passengerData?.profileData;
        nationality = 'Ethiopian';
      } else if (!isEthiopian && passenger.idDocumentType === IdDocumentType.PASSPORT) {
        if (!passenger.passportNumber || !passenger.passportCountry) throw new BadRequestException(`Passport details required for ${passenger.passengerName}`);
        nationality = nationality || (passenger.passportCountry === 'Djibouti' ? 'Djiboutian' : 'Other');
      } else {
        nationality = nationality || 'Other';
      }
      passengersData.push({ ...passenger, passengerName, dateOfBirth, category, verifaydaVerified, verifaydaData, nationality });
    }

    const leg2SeatClassId    = dto.leg2SeatClassId || dto.seatClassId;
    const primaryNationality = passengersData[0]?.nationality;
    const paidChildrenCount  = Math.max(0, childCount - 1);

    const [leg1BaseFare, leg2BaseFare] = await Promise.all([
      this.getBaseFare(dto.scheduleId, dto.seatClassId,
        `${leg1OriginStop.station.code}-${leg1DestStop.station.code}`,
        `${leg1Schedule.originStation.code}-${leg1Schedule.destinationStation.code}`,
        primaryNationality),
      this.getBaseFare(dto.leg2ScheduleId, leg2SeatClassId,
        `${leg2OriginStop.station.code}-${leg2DestStop.station.code}`,
        `${leg2Schedule.originStation.code}-${leg2Schedule.destinationStation.code}`,
        primaryNationality),
    ]);

    const leg1Total      = leg1BaseFare * adultCount + leg1BaseFare * paidChildrenCount;
    const leg2Total      = leg2BaseFare * adultCount + leg2BaseFare * paidChildrenCount;
    const combinedBase   = leg1Total + leg2Total;

    let discountMinor = 0;
    if (dto.promoCode) {
      const promo = await this.prisma.promotion.findUnique({ where: { code: dto.promoCode } });
      if (promo?.active && promo.validUntil > new Date()) {
        discountMinor = promo.percentOff ? Math.round(combinedBase * promo.percentOff / 100) : (promo.amountOffMinor ?? 0);
      }
    }
    const taxesMinor = Math.round(combinedBase * 0.05);
    const totalMinor = Math.max(0, combinedBase - discountMinor + taxesMinor);

    const displayCurrency = dto.displayCurrency || Currency.ETB;
    const displayTotalMinor = displayCurrency !== Currency.ETB
      ? await this.currencyService.convertAmount(totalMinor, Currency.ETB, displayCurrency)
      : totalMinor;

    const { guestPassenger, userId, createdAccount } = await this.resolveGuestPassenger(dto, passengersData[0]);

    // Single booking — leg-1 seats at leg=1, leg-2 seats at leg=2
    const booking = await this.prisma.booking.create({
      data: {
        bookingRef:              generateRef(),
        passengerId:             guestPassenger.id,
        scheduleId:              dto.scheduleId,
        status:                  'PENDING_PAYMENT',
        bookingType:             'TRANSIT',
        totalMinor,
        adultCount,
        childCount,
        displayCurrency,
        displayTotalMinor,
        leg2ScheduleId:          dto.leg2ScheduleId,
        leg2OriginStationId:     dto.transitStationId,
        leg2DestinationStationId: dto.leg2DestinationStationId,
        leg2SeatClassId:         leg2SeatClassId,
        userAgent:               dto.deviceId,
        contactEmail:            passengersData[0]?.email || null,
        contactPhone:            passengersData[0]?.phone || null,
        seats: {
          create: [
            ...passengersData.map(p => ({
              seat:              { connect: { id: p.seatId } },
              leg:               1,
              scheduleId:        dto.scheduleId,
              passengerName:     p.passengerName,
              dateOfBirth:       p.dateOfBirth,
              passengerCategory: p.category,
              idDocumentType:    p.idDocumentType,
              passportNumber:    p.passportNumber,
              passportCountry:   p.passportCountry,
              verifaydaVerified: p.verifaydaVerified,
              verifaydaData:     p.verifaydaData || undefined,
              fareMinor:         p.category === PassengerCategory.ADULT ? leg1BaseFare : (paidChildrenCount > 0 ? leg1BaseFare : 0),
              displayCurrency,
            })),
            ...passengersData.map(p => ({
              seat:              { connect: { id: p.leg2SeatId! } },
              leg:               2,
              scheduleId:        dto.leg2ScheduleId,
              passengerName:     p.passengerName,
              dateOfBirth:       p.dateOfBirth,
              passengerCategory: p.category,
              idDocumentType:    p.idDocumentType,
              passportNumber:    p.passportNumber,
              passportCountry:   p.passportCountry,
              verifaydaVerified: p.verifaydaVerified,
              verifaydaData:     p.verifaydaData || undefined,
              fareMinor:         p.category === PassengerCategory.ADULT ? leg2BaseFare : (paidChildrenCount > 0 ? leg2BaseFare : 0),
              displayCurrency,
            })),
          ],
        },
      } as any,
      include: {
        seats:    { include: { seat: { include: { coach: true } } } },
        schedule: { include: { originStation: true, destinationStation: true, train: true } },
      },
    });

    await Promise.all([
      this.seatsService.confirmSeats(dto.passengers.map(p => p.seatId)),
      this.seatsService.confirmSeats(dto.passengers.map(p => p.leg2SeatId!)),
    ]);
    this.eventEmitter.emit('booking.created', { booking });

    return {
      ...booking,
      createdAccount,
      userId,
      fareBreakdown: {
        leg1BaseFareMinor: leg1BaseFare,
        leg2BaseFareMinor: leg2BaseFare,
        adultCount, childCount,
        freeChildrenCount: Math.min(childCount, 1),
        paidChildrenCount,
        combinedBaseFareMinor: combinedBase,
        discountMinor, taxesFeesMinor: taxesMinor, totalMinor,
        currency: 'ETB', displayCurrency, displayTotalMinor,
      },
    };
  }

  private async createGuestRoundTripTransitBooking(dto: CreateGuestBookingDto) {
    if (!dto.leg2ScheduleId || !dto.leg2HoldId || !dto.transitStationId || !dto.leg2DestinationStationId ||
        !dto.returnScheduleId || !dto.returnHoldId || !dto.returnOriginStationId || !dto.returnDestinationStationId ||
        !dto.returnLeg2ScheduleId || !dto.returnLeg2HoldId || !dto.returnTransitStationId || !dto.returnLeg2DestinationStationId) {
      throw new BadRequestException(
        'ROUND_TRIP_TRANSIT requires all 4 holds and all transit/return station fields',
      );
    }
    for (const p of dto.passengers) {
      if (!p.leg2SeatId)      throw new BadRequestException(`leg2SeatId required for ${p.passengerName}`);
      if (!p.returnSeatId)    throw new BadRequestException(`returnSeatId required for ${p.passengerName}`);
      if (!p.returnLeg2SeatId) throw new BadRequestException(`returnLeg2SeatId required for ${p.passengerName}`);
    }

    const now = new Date();
    const [obL1Hold, obL2Hold, retL1Hold, retL2Hold] = await Promise.all([
      this.prisma.seatHold.findUnique({ where: { id: dto.holdId } }),
      this.prisma.seatHold.findUnique({ where: { id: dto.leg2HoldId } }),
      this.prisma.seatHold.findUnique({ where: { id: dto.returnHoldId } }),
      this.prisma.seatHold.findUnique({ where: { id: dto.returnLeg2HoldId } }),
    ]);
    if (!obL1Hold  || obL1Hold.expiresAt  < now) throw new BadRequestException('Outbound leg-1 hold expired');
    if (!obL2Hold  || obL2Hold.expiresAt  < now) throw new BadRequestException('Outbound leg-2 hold expired');
    if (!retL1Hold || retL1Hold.expiresAt < now) throw new BadRequestException('Return leg-1 hold expired');
    if (!retL2Hold || retL2Hold.expiresAt < now) throw new BadRequestException('Return leg-2 hold expired');

    const [obL1Sched, obL2Sched, retL1Sched, retL2Sched] = await Promise.all([
      this.prisma.trainSchedule.findUnique({ where: { id: dto.scheduleId },          include: { originStation: true, destinationStation: true, stopTimes: { include: { station: true }, orderBy: { sequence: 'asc' } } } }),
      this.prisma.trainSchedule.findUnique({ where: { id: dto.leg2ScheduleId },      include: { originStation: true, destinationStation: true, stopTimes: { include: { station: true }, orderBy: { sequence: 'asc' } } } }),
      this.prisma.trainSchedule.findUnique({ where: { id: dto.returnScheduleId },    include: { originStation: true, destinationStation: true, stopTimes: { include: { station: true }, orderBy: { sequence: 'asc' } } } }),
      this.prisma.trainSchedule.findUnique({ where: { id: dto.returnLeg2ScheduleId },include: { originStation: true, destinationStation: true, stopTimes: { include: { station: true }, orderBy: { sequence: 'asc' } } } }),
    ]);
    if (!obL1Sched)  throw new NotFoundException('Outbound leg-1 schedule not found');
    if (!obL2Sched)  throw new NotFoundException('Outbound leg-2 schedule not found');
    if (!retL1Sched) throw new NotFoundException('Return leg-1 schedule not found');
    if (!retL2Sched) throw new NotFoundException('Return leg-2 schedule not found');

    const obL1Origin  = obL1Sched.stopTimes.find(s => s.stationId === dto.originStationId);
    const obL1Dest    = obL1Sched.stopTimes.find(s => s.stationId === dto.transitStationId);
    const obL2Origin  = obL2Sched.stopTimes.find(s => s.stationId === dto.transitStationId);
    const obL2Dest    = obL2Sched.stopTimes.find(s => s.stationId === dto.leg2DestinationStationId);
    const retL1Origin = retL1Sched.stopTimes.find(s => s.stationId === dto.returnOriginStationId);
    const retL1Dest   = retL1Sched.stopTimes.find(s => s.stationId === dto.returnTransitStationId);
    const retL2Origin = retL2Sched.stopTimes.find(s => s.stationId === dto.returnTransitStationId);
    const retL2Dest   = retL2Sched.stopTimes.find(s => s.stationId === dto.returnLeg2DestinationStationId);
    if (!obL1Origin || !obL1Dest)   throw new NotFoundException('Outbound leg-1: origin or transit stop not found');
    if (!obL2Origin || !obL2Dest)   throw new NotFoundException('Outbound leg-2: transit or destination stop not found');
    if (!retL1Origin || !retL1Dest) throw new NotFoundException('Return leg-1: origin or transit stop not found');
    if (!retL2Origin || !retL2Dest) throw new NotFoundException('Return leg-2: transit or destination stop not found');

    // Process passengers (verify once)
    const passengersData: any[] = [];
    let adultCount = 0, childCount = 0;
    for (const passenger of dto.passengers) {
      const dateOfBirth = new Date(passenger.dateOfBirth);
      const category: PassengerCategory = calculateAge(dateOfBirth) < 5 ? PassengerCategory.CHILD : PassengerCategory.ADULT;
      if (category === PassengerCategory.ADULT) adultCount++; else childCount++;
      let passengerName = passenger.passengerName;
      let verifaydaVerified = false;
      let verifaydaData: Record<string, any> | undefined;
      let nationality = passenger.nationality;
      const isEthiopian = nationality === 'Ethiopian' || nationality === 'ETHIOPIAN' || passenger.idDocumentType === IdDocumentType.NATIONAL_ID;
      if (isEthiopian && passenger.idDocumentType === IdDocumentType.NATIONAL_ID && passenger.idDocumentNumber) {
        const v = await this.verifaydaService.verifyNationalId(passenger.idDocumentNumber);
        if (!v.verified) throw new BadRequestException(`Verifayda failed for ${passenger.passengerName}: ${v.failureReason}`);
        passengerName = v.passengerData?.fullName || passengerName;
        verifaydaVerified = true;
        verifaydaData = v.passengerData?.profileData;
        nationality = 'Ethiopian';
      } else if (!isEthiopian && passenger.idDocumentType === IdDocumentType.PASSPORT) {
        if (!passenger.passportNumber || !passenger.passportCountry) throw new BadRequestException(`Passport details required for ${passenger.passengerName}`);
        nationality = nationality || (passenger.passportCountry === 'Djibouti' ? 'Djiboutian' : 'Other');
      } else {
        nationality = nationality || 'Other';
      }
      passengersData.push({ ...passenger, passengerName, dateOfBirth, category, verifaydaVerified, verifaydaData, nationality });
    }

    const nat            = passengersData[0]?.nationality;
    const paidChildren   = Math.max(0, childCount - 1);
    const obL2ClassId    = dto.leg2SeatClassId      ?? dto.seatClassId;
    const retL1ClassId   = dto.returnSeatClassId    ?? dto.seatClassId;
    const retL2ClassId   = dto.returnLeg2SeatClassId ?? dto.seatClassId;

    const [obL1Fare, obL2Fare, retL1Fare, retL2Fare] = await Promise.all([
      this.getBaseFare(dto.scheduleId,           dto.seatClassId, `${obL1Origin.station.code}-${obL1Dest.station.code}`,   `${obL1Sched.originStation.code}-${obL1Sched.destinationStation.code}`,   nat),
      this.getBaseFare(dto.leg2ScheduleId!,      obL2ClassId,     `${obL2Origin.station.code}-${obL2Dest.station.code}`,   `${obL2Sched.originStation.code}-${obL2Sched.destinationStation.code}`,   nat),
      this.getBaseFare(dto.returnScheduleId!,    retL1ClassId,    `${retL1Origin.station.code}-${retL1Dest.station.code}`, `${retL1Sched.originStation.code}-${retL1Sched.destinationStation.code}`, nat),
      this.getBaseFare(dto.returnLeg2ScheduleId!,retL2ClassId,    `${retL2Origin.station.code}-${retL2Dest.station.code}`, `${retL2Sched.originStation.code}-${retL2Sched.destinationStation.code}`, nat),
    ]);

    const combinedBase = (obL1Fare + obL2Fare + retL1Fare + retL2Fare) * adultCount +
                         (obL1Fare + obL2Fare + retL1Fare + retL2Fare) * paidChildren;
    let discountMinor = 0;
    if (dto.promoCode) {
      const promo = await this.prisma.promotion.findUnique({ where: { code: dto.promoCode } });
      if (promo?.active && promo.validUntil > new Date()) {
        discountMinor = promo.percentOff ? Math.round(combinedBase * promo.percentOff / 100) : (promo.amountOffMinor ?? 0);
      }
    }
    const taxesMinor      = Math.round(combinedBase * 0.05);
    const totalMinor      = Math.max(0, combinedBase - discountMinor + taxesMinor);
    const displayCurrency = dto.displayCurrency || Currency.ETB;
    const displayTotalMinor = displayCurrency !== Currency.ETB
      ? await this.currencyService.convertAmount(totalMinor, Currency.ETB, displayCurrency)
      : totalMinor;

    const { guestPassenger, userId, createdAccount } = await this.resolveGuestPassenger(dto, passengersData[0]);

    const makeSeat = (p: any, seatId: string, leg: number, scheduleId: string, fare: number) => ({
      seat:              { connect: { id: seatId } },
      leg,
      scheduleId,
      passengerName:     p.passengerName,
      dateOfBirth:       p.dateOfBirth,
      passengerCategory: p.category,
      idDocumentType:    p.idDocumentType,
      passportNumber:    p.passportNumber,
      passportCountry:   p.passportCountry,
      verifaydaVerified: p.verifaydaVerified,
      verifaydaData:     p.verifaydaData || undefined,
      fareMinor:         p.category === PassengerCategory.ADULT ? fare : (paidChildren > 0 ? fare : 0),
      displayCurrency,
    });

    const booking = await this.prisma.booking.create({
      data: {
        bookingRef:                   generateRef(),
        passengerId:                  guestPassenger.id,
        scheduleId:                   dto.scheduleId,
        status:                       'PENDING_PAYMENT',
        bookingType:                  'ROUND_TRIP_TRANSIT',
        totalMinor, adultCount, childCount, displayCurrency, displayTotalMinor,
        leg2ScheduleId:               dto.leg2ScheduleId,
        leg2OriginStationId:          dto.transitStationId,
        leg2DestinationStationId:     dto.leg2DestinationStationId,
        leg2SeatClassId:              obL2ClassId,
        returnScheduleId:             dto.returnScheduleId,
        returnOriginStationId:        dto.returnOriginStationId,
        returnDestinationStationId:   dto.returnDestinationStationId,
        returnSeatClassId:            retL1ClassId,
        returnLeg2ScheduleId:         dto.returnLeg2ScheduleId,
        returnLeg2OriginStationId:    dto.returnTransitStationId,
        returnLeg2DestStationId:      dto.returnLeg2DestinationStationId,
        returnLeg2SeatClassId:        retL2ClassId,
        returnLegStatus:              'NEITHER_USED',
        userAgent:                    dto.deviceId,
        contactEmail:                 passengersData[0]?.email || null,
        contactPhone:                 passengersData[0]?.phone || null,
        seats: {
          create: [
            ...passengersData.map(p => makeSeat(p, p.seatId,          1, dto.scheduleId,            obL1Fare)),
            ...passengersData.map(p => makeSeat(p, p.leg2SeatId!,     2, dto.leg2ScheduleId!,       obL2Fare)),
            ...passengersData.map(p => makeSeat(p, p.returnSeatId!,   3, dto.returnScheduleId!,     retL1Fare)),
            ...passengersData.map(p => makeSeat(p, p.returnLeg2SeatId!,4, dto.returnLeg2ScheduleId!,retL2Fare)),
          ],
        },
      } as any,
      include: {
        seats:    { include: { seat: { include: { coach: true } } } },
        schedule: { include: { originStation: true, destinationStation: true, train: true } },
      },
    });

    await Promise.all([
      this.seatsService.confirmSeats(dto.passengers.map(p => p.seatId)),
      this.seatsService.confirmSeats(dto.passengers.map(p => p.leg2SeatId!)),
      this.seatsService.confirmSeats(dto.passengers.map(p => p.returnSeatId!)),
      this.seatsService.confirmSeats(dto.passengers.map(p => p.returnLeg2SeatId!)),
    ]);
    this.eventEmitter.emit('booking.created', { booking });

    return {
      ...booking,
      createdAccount,
      userId,
      fareBreakdown: {
        outboundLeg1FareMinor: obL1Fare,
        outboundLeg2FareMinor: obL2Fare,
        returnLeg1FareMinor:   retL1Fare,
        returnLeg2FareMinor:   retL2Fare,
        adultCount, childCount,
        freeChildrenCount: Math.min(childCount, 1),
        paidChildrenCount: paidChildren,
        combinedBaseFareMinor: combinedBase,
        discountMinor, taxesFeesMinor: taxesMinor, totalMinor,
        currency: 'ETB', displayCurrency, displayTotalMinor,
      },
    };
  }

  private async resolveGuestPassenger(
    dto: Pick<CreateGuestBookingDto, 'createAccount' | 'password' | 'deviceId'>,
    firstPassenger: any,
  ): Promise<{ guestPassenger: any; userId: string | null; createdAccount: boolean }> {
    if (dto.createAccount && firstPassenger.email && dto.password) {
      const existingUser = await this.prisma.user.findUnique({ where: { email: firstPassenger.email } });
      if (existingUser) throw new BadRequestException('Email already registered. Please login instead.');

      let accountPhone = firstPassenger.phone || null;
      if (accountPhone) {
        const existingPhone = await this.prisma.user.findUnique({ where: { phone: accountPhone } });
        if (existingPhone) throw new BadRequestException('Phone number already registered. Please login instead.');
      }
      if (!accountPhone) accountPhone = `+guest-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;

      const user = await this.prisma.user.create({
        data: {
          fullName:      firstPassenger.passengerName,
          email:         firstPassenger.email,
          phone:         accountPhone,
          passwordHash:  await bcrypt.hash(dto.password, 10),
          nationality:   firstPassenger.nationality,
          nationalId:    firstPassenger.idDocumentType === IdDocumentType.NATIONAL_ID ? firstPassenger.idDocumentNumber : undefined,
          passportNumber: firstPassenger.passportNumber,
        },
      });
      const guestPassenger = await this.prisma.passenger.create({ data: { userId: user.id } });
      await this.prisma.loyaltyAccount.create({ data: { passengerId: guestPassenger.id, pointsBalance: 0, tier: 'BRONZE' } });
      await this.prisma.walletAccount.create({ data: { passengerId: guestPassenger.id, balanceMinor: 0 } });
      return { guestPassenger, userId: user.id, createdAccount: true };
    }

    const uniqueId = `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;

    let guestEmail = firstPassenger.email;
    if (guestEmail) {
      const existing = await this.prisma.user.findUnique({ where: { email: guestEmail } });
      if (existing) guestEmail = null;
    }
    if (!guestEmail) guestEmail = `guest-${uniqueId}@edr-platform.com`;

    let guestPhone = firstPassenger.phone;
    if (guestPhone) {
      const existing = await this.prisma.user.findUnique({ where: { phone: guestPhone } });
      if (existing) guestPhone = null;
    }
    if (!guestPhone) guestPhone = `+guest-${uniqueId}`;

    const tempUser = await this.prisma.user.create({
      data: {
        fullName:     firstPassenger.passengerName,
        email:        guestEmail,
        phone:        guestPhone,
        passwordHash: await bcrypt.hash(Math.random().toString(36), 10),
        role:         'PASSENGER',
      },
    });
    const guestPassenger = await this.prisma.passenger.create({ data: { userId: tempUser.id } });
    return { guestPassenger, userId: null, createdAccount: false };
  }

  async getSavedPassengers(userId?: string, deviceId?: string): Promise<SavedPassengerProfileDto[]> {
    if (!userId && !deviceId) {
      throw new BadRequestException('Either userId or deviceId is required');
    }

    // Temporarily return empty array until Prisma client is regenerated
    return [];

    /* Uncomment after running migration and prisma generate
    const profiles = await this.prisma.savedPassengerProfile.findMany({
      where: {
        OR: [
          userId ? { userId } : {},
          deviceId ? { deviceId } : {},
        ],
      },
      orderBy: { createdAt: 'desc' },
    });

    return profiles.map((p: any) => ({
      passengerName: p.passengerName,
      dateOfBirth: p.dateOfBirth.toISOString().split('T')[0],
      idDocumentType: p.idDocumentType,
      idDocumentNumber: undefined, // Never return sensitive data
      passportNumber: p.passportNumber || undefined,
      passportCountry: p.passportCountry || undefined,
      nationality: p.nationality || undefined,
      phone: p.phone || undefined,
      email: p.email || undefined,
    }));
    */
  }

  private async getBaseFare(
    scheduleId: string,
    seatClassId: string,
    segmentRoute?: string,
    fullRoute?: string,
    nationality?: string,
  ): Promise<number> {
    const now = new Date();

    // 1. FareRule table — explicit override rules
    const candidates = await this.prisma.fareRule.findMany({
      where: {
        seatClassId,
        validFrom: { lte: now },
        OR: [{ validUntil: null }, { validUntil: { gte: now } }],
      },
    });

    const priorities = [
      { tripId: scheduleId, route: segmentRoute, nationality },
      { tripId: scheduleId, route: segmentRoute, nationality: null },
      { tripId: scheduleId, route: fullRoute, nationality },
      { tripId: scheduleId, route: fullRoute, nationality: null },
      { tripId: scheduleId, route: null, nationality },
      { tripId: scheduleId, route: null, nationality: null },
      { tripId: null, route: segmentRoute, nationality },
      { tripId: null, route: segmentRoute, nationality: null },
      { tripId: null, route: fullRoute, nationality },
      { tripId: null, route: fullRoute, nationality: null },
      { tripId: null, route: null, nationality },
      { tripId: null, route: null, nationality: null },
    ];

    for (const priority of priorities) {
      const match = candidates.find(
        (c) => c.tripId === priority.tripId && c.route === priority.route && c.nationality === priority.nationality,
      );
      if (match) return match.baseFareMinor;
    }

    // 2. FareEngine — distance × rate-per-km from the schedule's route
    const schedule = await this.prisma.trainSchedule.findUnique({
      where: { id: scheduleId },
      select: { routeId: true, originStationId: true, destinationStationId: true },
    });

    if (schedule?.routeId) {
      try {
        const fare = await this.fareEngine.calculate({
          routeId: schedule.routeId,
          originStationId: schedule.originStationId,
          destinationStationId: schedule.destinationStationId,
          seatClassId,
          nationality,
        });
        return fare.baseFarePerPassengerMinor;
      } catch {
        // FareEngine throws if distanceKm is missing; fall through to error
      }
    }

    throw new BadRequestException(
      `No fare configured for this schedule and seat class. Please set up fare rules or route distances.`,
    );
  }
}
