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 { 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 eventEmitter: EventEmitter2,
  ) {}

  async createGuestBooking(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];
    let guestPassenger = null;
    let userId = null;
    let createdAccount = false;

    // Optional account creation
    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 passwordHash = await bcrypt.hash(dto.password, 10);
      const user = await this.prisma.user.create({
        data: {
          fullName: firstPassenger.passengerName,
          email: firstPassenger.email,
          phone: accountPhone,
          passwordHash,
          nationality: firstPassenger.nationality,
          nationalId: firstPassenger.idDocumentType === IdDocumentType.NATIONAL_ID ? firstPassenger.idDocumentNumber : undefined,
          passportNumber: firstPassenger.passportNumber,
        },
      });

      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 } });
      
      userId = user.id;
      createdAccount = true;
    } else {
      // Create anonymous guest passenger with minimal data
      const uniqueId = `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
      
      // Check if email exists and use a unique guest email if it does
      let guestEmail = firstPassenger.email || `guest-${uniqueId}@edr-platform.com`;
      if (firstPassenger.email) {
        const existingUser = await this.prisma.user.findUnique({ where: { email: firstPassenger.email } });
        if (existingUser) {
          // Email exists, use guest email instead for anonymous booking
          guestEmail = `guest-${uniqueId}@edr-platform.com`;
        }
      }
      
      // Use a guaranteed-unique guest phone to avoid constraint collisions
      let guestPhone = firstPassenger.phone || null;
      if (guestPhone) {
        const existingPhone = await this.prisma.user.findUnique({ where: { phone: guestPhone } });
        if (existingPhone) 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',
        },
      });
      guestPassenger = await this.prisma.passenger.create({ data: { userId: tempUser.id } });
    }

    // 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,  // Temporarily disabled until migration
        // contactPhone: firstPassenger.phone,  // Temporarily disabled until migration
        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,
      },
    };
  }

  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();
    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;
    }

    return 35000; // Default fallback
  }
}
