import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common';
import { PrismaService } from '../../common/prisma.service';
import { CreateAgentBookingDto, OpenShiftDto, CloseShiftDto } from './agents.dto';
import { IdDocumentType } from '@prisma/client';

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

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

  async createAgentBooking(dto: CreateAgentBookingDto) {
    const agent = await this.prisma.agent.findUnique({ where: { id: dto.agentId }, include: { user: { include: { passenger: true } } } });
    if (!agent || !agent.active) throw new NotFoundException('Agent not found or inactive');
    if (!agent.user.passenger) throw new BadRequestException('Agent must have passenger account');

    const schedule = await this.prisma.trainSchedule.findUnique({ where: { id: dto.scheduleId } });
    if (!schedule) throw new NotFoundException('Schedule not found');

    const seatIds = dto.passengers.map(p => p.seatId);
    const seats = await this.prisma.seat.findMany({ where: { id: { in: seatIds } } });
    if (seats.length !== seatIds.length) throw new BadRequestException('Invalid seat selection');

    const baseFare = 45000 * dto.passengers.length;
    const totalMinor = baseFare;

    const booking = await this.prisma.booking.create({
      data: {
        bookingRef: generateRef(),
        passengerId: agent.user.passenger.id,
        scheduleId: dto.scheduleId,
        status: dto.paymentMethod === 'CASH' ? 'CONFIRMED' : 'PENDING_PAYMENT',
        totalMinor,
        seats: {
          create: dto.passengers.map(p => ({
            seat: { connect: { id: p.seatId } },
            passengerName: p.fullName,
            idDocumentType: p.idDocumentType as IdDocumentType | undefined,
            idDocumentNumber: p.idDocumentNumber
          }))
        }
      },
      include: { seats: true }
    });

    await this.prisma.seat.updateMany({
      where: { id: { in: seatIds } },
      data: { status: 'BOOKED' }
    });

    const changeGiven = dto.cashReceived ? dto.cashReceived - totalMinor : 0;
    await this.prisma.agentBooking.create({
      data: {
        agentId: dto.agentId,
        bookingId: booking.id,
        paymentMethod: dto.paymentMethod,
        cashReceived: dto.cashReceived,
        changeGiven,
        paperTicket: dto.paperTicket ?? false
      }
    });

    const commissionAmount = Math.floor(totalMinor * agent.commissionRate / 100);
    await this.prisma.agentCommission.create({
      data: {
        agentId: dto.agentId,
        bookingId: booking.id,
        amountMinor: commissionAmount,
        rate: agent.commissionRate
      }
    });

    return { booking, commission: commissionAmount };
  }

  async openShift(dto: OpenShiftDto) {
    const agent = await this.prisma.agent.findUnique({ where: { id: dto.agentId } });
    if (!agent) throw new NotFoundException('Agent not found');

    const openShift = await this.prisma.agentShift.findFirst({
      where: { agentId: dto.agentId, closedAt: null }
    });
    if (openShift) throw new BadRequestException('Shift already open');

    return this.prisma.agentShift.create({
      data: {
        agentId: dto.agentId,
        openingBalance: dto.openingBalance ?? 0
      }
    });
  }

  async closeShift(dto: CloseShiftDto) {
    const shift = await this.prisma.agentShift.findUnique({ where: { id: dto.shiftId } });
    if (!shift) throw new NotFoundException('Shift not found');
    if (shift.closedAt) throw new BadRequestException('Shift already closed');

    return this.prisma.agentShift.update({
      where: { id: dto.shiftId },
      data: {
        closedAt: new Date(),
        closingBalance: dto.closingBalance,
        notes: dto.notes,
        reconciled: true
      }
    });
  }

  async getCommissions(agentId: string, dateFrom?: Date, dateTo?: Date) {
    return this.prisma.agentCommission.findMany({
      where: {
        agentId,
        createdAt: {
          gte: dateFrom,
          lte: dateTo
        }
      },
      orderBy: { createdAt: 'desc' }
    });
  }

  async getShifts(agentId: string) {
    return this.prisma.agentShift.findMany({
      where: { agentId },
      orderBy: { openedAt: 'desc' },
      take: 20
    });
  }
}
