import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common';
import { PrismaService } from '../../common/prisma.service';
import * as QRCode from 'qrcode';

interface OfflineValidation {
  bookingRef: string;
  validatorId: string;
  gateId?: string;
  validatedAt: string;
}

@Injectable()
export class TicketsService {
  constructor(private prisma: PrismaService) {}

  async generate(bookingId: string) {
    const booking = await this.prisma.booking.findUnique({
      where: { id: bookingId },
      include: { schedule: { include: { originStation: true, destinationStation: true, train: true } }, seats: { include: { seat: { include: { coach: true } } } } },
    });
    if (!booking) throw new NotFoundException('Booking not found');
    const qrPayload = await QRCode.toDataURL(`edr:tkt:${booking.id}:${booking.bookingRef}`);
    const barcodePayload = `EDR${booking.bookingRef}${booking.id.substring(0, 8).toUpperCase()}`;
    return this.prisma.ticket.upsert({ 
      where: { bookingId }, 
      update: { qrPayload, barcodePayload }, 
      create: { bookingId, bookingRef: booking.bookingRef, qrPayload, barcodePayload } 
    });
  }

  async getByRef(bookingRef: string) {
    const booking = await this.prisma.booking.findUnique({
      where: { bookingRef },
      include: { schedule: { include: { originStation: true, destinationStation: true, train: true } }, seats: { include: { seat: { include: { coach: true } } } }, ticket: true },
    });
    if (!booking?.ticket) throw new NotFoundException('Ticket not found');
    const seat = booking.seats[0];
    return {
      id: booking.ticket.id, bookingId: booking.id, bookingRef: booking.bookingRef, status: booking.status,
      fromStationName: booking.schedule.originStation.name, toStationName: booking.schedule.destinationStation.name,
      departureAt: booking.schedule.departureAt, trainName: booking.schedule.train.name,
      coachLabel: seat?.seat.coach.label, seatLabel: seat?.seat.label, passengerName: seat?.passengerName,
      priceMinor: booking.totalMinor, currency: booking.currency, qrPayload: booking.ticket.qrPayload,
      barcodePayload: booking.ticket.barcodePayload
    };
  }

  async validate(bookingRef: string, validatorId: string, gateId?: string) {
    const booking = await this.prisma.booking.findUnique({ where: { bookingRef } });
    if (!booking) throw new NotFoundException('Booking not found');
    const ticket = await this.prisma.ticket.findUnique({ where: { bookingId: booking.id } });
    if (!ticket) throw new NotFoundException('Ticket not found');
    if (ticket.validatedAt) {
      await this.prisma.gateValidationLog.create({
        data: { ticketId: ticket.id, validatorId, gateId, status: 'REJECTED', reason: 'ALREADY_VALIDATED' }
      });
      throw new BadRequestException('Ticket already validated');
    }
    await this.prisma.ticket.update({ where: { id: ticket.id }, data: { validatedAt: new Date(), validatorId } });
    await this.prisma.gateValidationLog.create({
      data: { ticketId: ticket.id, validatorId, gateId, status: 'APPROVED' }
    });
    return { validated: true, ticketId: ticket.id, validatedAt: new Date() };
  }

  async getValidationLogs(ticketId: string) {
    return this.prisma.gateValidationLog.findMany({
      where: { ticketId },
      orderBy: { validatedAt: 'desc' }
    });
  }

  async exportOfflineData(tripId: string) {
    const bookings = await this.prisma.booking.findMany({
      where: { scheduleId: tripId, status: 'CONFIRMED' },
      include: {
        ticket: true,
        seats: { include: { seat: { include: { coach: true } } } },
        passenger: { include: { user: true } },
      },
    });

    return bookings.map((b) => ({
      bookingRef: b.bookingRef,
      ticketId: b.ticket?.id,
      passengerName: b.seats[0]?.passengerName,
      seatLabel: b.seats[0]?.seat.label,
      coachLabel: b.seats[0]?.seat.coach.label,
      qrPayload: b.ticket?.qrPayload,
      status: b.status,
      validatedAt: b.ticket?.validatedAt,
    }));
  }

  async validateOfflineBatch(validations: OfflineValidation[]) {
    const results = { success: 0, failed: 0, duplicate: 0, errors: [] as string[] };
    const processedRefs = new Set<string>();

    for (const v of validations) {
      if (processedRefs.has(v.bookingRef)) {
        results.duplicate++;
        continue;
      }
      processedRefs.add(v.bookingRef);

      try {
        const booking = await this.prisma.booking.findUnique({ where: { bookingRef: v.bookingRef } });
        if (!booking) {
          results.failed++;
          results.errors.push(`Booking ${v.bookingRef} not found`);
          continue;
        }

        const ticket = await this.prisma.ticket.findUnique({ where: { bookingId: booking.id } });
        if (!ticket) {
          results.failed++;
          results.errors.push(`Ticket for ${v.bookingRef} not found`);
          continue;
        }

        if (ticket.validatedAt) {
          results.duplicate++;
          continue;
        }

        await this.prisma.ticket.update({
          where: { id: ticket.id },
          data: { validatedAt: new Date(v.validatedAt), validatorId: v.validatorId },
        });

        await this.prisma.gateValidationLog.create({
          data: {
            ticketId: ticket.id,
            validatorId: v.validatorId,
            gateId: v.gateId,
            status: 'APPROVED',
            validatedAt: new Date(v.validatedAt),
          },
        });

        results.success++;
      } catch (err) {
        results.failed++;
        results.errors.push(`Error processing ${v.bookingRef}: ${err instanceof Error ? err.message : String(err)}`);
      }
    }

    return results;
  }
}
