import { Injectable } from '@nestjs/common';
import { PrismaService } from '../../common/prisma.service';
import { GenerateReportDto, ReportType } from './reports.dto';

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

  async generateReport(dto: GenerateReportDto) {
    const dateFrom = new Date(dto.dateFrom);
    dateFrom.setHours(0, 0, 0, 0);
    
    const dateTo = new Date(dto.dateTo);
    dateTo.setHours(23, 59, 59, 999);

    let data: any;
    switch (dto.reportType) {
      case ReportType.REVENUE:
        data = await this.generateRevenueReport(dateFrom, dateTo);
        break;
      case ReportType.OCCUPANCY:
        data = await this.generateOccupancyReport(dateFrom, dateTo);
        break;
      case ReportType.AGENT_SALES:
        data = await this.generateAgentSalesReport(dateFrom, dateTo, dto.agentId);
        break;
      case ReportType.CANCELLATIONS:
        data = await this.generateCancellationsReport(dateFrom, dateTo);
        break;
      case ReportType.PAYMENT_METHODS:
        data = await this.generatePaymentMethodsReport(dateFrom, dateTo);
        break;
      default:
        data = {};
    }

    const report = await this.prisma.operationalReport.create({
      data: {
        reportType: dto.reportType,
        dateFrom,
        dateTo,
        data
      }
    });

    return { reportId: report.id, reportType: dto.reportType, data };
  }

  private async generateRevenueReport(dateFrom: Date, dateTo: Date) {
    // Fetch all bookings in date range, regardless of status
    const bookings = await this.prisma.booking.findMany({
      where: {
        createdAt: { gte: dateFrom, lte: dateTo }
      },
      include: { paymentIntent: true }
    });

    console.log(`[Reports] Revenue Report: Found ${bookings.length} bookings between ${dateFrom} and ${dateTo}`);

    const totalRevenue = bookings.reduce((sum, b) => sum + b.totalMinor, 0);
    const byPaymentMethod = bookings.reduce((acc, b) => {
      const method = b.paymentIntent?.method ?? 'UNKNOWN';
      acc[method] = (acc[method] || 0) + b.totalMinor;
      return acc;
    }, {} as Record<string, number>);

    // Group by date for charts
    const byDate = bookings.reduce((acc, b) => {
      const date = b.createdAt.toISOString().split('T')[0];
      if (!acc[date]) {
        acc[date] = { totalMinor: 0, count: 0 };
      }
      acc[date].totalMinor += b.totalMinor;
      acc[date].count += 1;
      return acc;
    }, {} as Record<string, any>);

    return {
      totalBookings: bookings.length,
      totalRevenueMinor: totalRevenue,
      totalRevenue: totalRevenue / 100,
      currency: 'ETB',
      byPaymentMethod,
      byDate,
      cancellationRate: 0
    };
  }

  private async generateOccupancyReport(dateFrom: Date, dateTo: Date) {
    const schedules = await this.prisma.trainSchedule.findMany({
      where: { departureAt: { gte: dateFrom, lte: dateTo } },
      include: {
        coachAssignments: { include: { coach: { include: { seats: true } } } },
        bookings: { include: { seats: true } },
      },
    });

    const tripData = schedules.map(schedule => {
      const totalSeats = schedule.coachAssignments.reduce((sum, a) => sum + a.coach.seats.length, 0);
      const bookedSeats = schedule.bookings.reduce((sum, b) => sum + b.seats.length, 0);
      const occupancyRate = totalSeats > 0 ? (bookedSeats / totalSeats) * 100 : 0;
      return { scheduleId: schedule.id, departureAt: schedule.departureAt, totalSeats, bookedSeats, occupancyRate: +occupancyRate.toFixed(2) };
    });

    const avgOccupancy = tripData.length > 0 ? tripData.reduce((sum, t) => sum + t.occupancyRate, 0) / tripData.length : 0;
    return { totalSchedules: schedules.length, averageOccupancyRate: +avgOccupancy.toFixed(2), schedules: tripData };
  }

  private async generateAgentSalesReport(dateFrom: Date, dateTo: Date, agentId?: string) {
    const agentBookings = await this.prisma.agentBooking.findMany({
      where: {
        createdAt: { gte: dateFrom, lte: dateTo },
        ...(agentId ? { agentId } : {})
      },
      include: {
        agent: { include: { user: true } },
        booking: true
      }
    });

    const byAgent = agentBookings.reduce((acc, ab) => {
      const agentName = ab.agent.user.fullName;
      if (!acc[agentName]) {
        acc[agentName] = { bookings: 0, revenueMinor: 0, cashCollected: 0 };
      }
      acc[agentName].bookings += 1;
      acc[agentName].revenueMinor += ab.booking.totalMinor;
      acc[agentName].cashCollected += ab.cashReceived ?? 0;
      return acc;
    }, {} as Record<string, any>);

    return {
      totalAgentBookings: agentBookings.length,
      byAgent
    };
  }

  private async generateCancellationsReport(dateFrom: Date, dateTo: Date) {
    const cancellations = await this.prisma.bookingCancellation.findMany({
      where: { createdAt: { gte: dateFrom, lte: dateTo } },
      include: { booking: true }
    });

    const totalRefunded = cancellations.reduce((sum, c) => sum + c.refundAmount, 0);

    return {
      totalCancellations: cancellations.length,
      totalRefundedMinor: totalRefunded,
      totalRefunded: totalRefunded / 100,
      currency: 'ETB'
    };
  }

  private async generatePaymentMethodsReport(dateFrom: Date, dateTo: Date) {
    const payments = await this.prisma.paymentIntent.findMany({
      where: {
        createdAt: { gte: dateFrom, lte: dateTo },
        status: 'SUCCEEDED'
      }
    });

    const byMethod = payments.reduce((acc, p) => {
      const method = p.method;
      if (!acc[method]) {
        acc[method] = { count: 0, totalMinor: 0 };
      }
      acc[method].count += 1;
      acc[method].totalMinor += p.amountMinor;
      return acc;
    }, {} as Record<string, any>);

    return {
      totalPayments: payments.length,
      byMethod
    };
  }

  async getReport(reportId: string) {
    return this.prisma.operationalReport.findUnique({ where: { id: reportId } });
  }

  async listReports(reportType?: string) {
    return this.prisma.operationalReport.findMany({
      where: reportType ? { reportType } : {},
      orderBy: { createdAt: 'desc' },
      take: 50
    });
  }
}
