import { Injectable } from '@nestjs/common';
import { PrismaService } from '../../../prisma/prisma.service';

export interface BookingSummary {
  totalRecords: number;
  byStatus: Record<string, number>;
  bySource: Record<string, number>;
  revenue: number;
  paidAmount: number;
  avgRecordsPerDay: number;
}

export interface StaffUtilization {
  staffId: string;
  staffName: string;
  totalRecords: number;
  completedRecords: number;
  noShowRecords: number;
  completionRate: number;
  totalRevenue: number;
}

export interface PopularService {
  serviceId: string;
  serviceName: string;
  totalBookings: number;
  revenue: number;
  avgRating?: number;
}

export interface DailyTrend {
  date: string;
  total: number;
  completed: number;
  cancelled: number;
  noShow: number;
  revenue: number;
}

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

  async getSummary(clubId: string, from: Date, to: Date): Promise<BookingSummary> {
    const records = await this.prisma.bookingRecord.findMany({
      where: {
        clubId,
        startAt: { gte: from, lte: to },
      },
      select: {
        status: true,
        source: true,
        price: true,
        paidAmount: true,
        startAt: true,
      },
    });

    const byStatus: Record<string, number> = {};
    const bySource: Record<string, number> = {};
    let revenue = 0;
    let paidAmount = 0;

    for (const r of records) {
      byStatus[r.status] = (byStatus[r.status] || 0) + 1;
      bySource[r.source] = (bySource[r.source] || 0) + 1;
      revenue += Number(r.price);
      paidAmount += Number(r.paidAmount);
    }

    const days = Math.max(1, Math.ceil((to.getTime() - from.getTime()) / (1000 * 60 * 60 * 24)));

    return {
      totalRecords: records.length,
      byStatus,
      bySource,
      revenue: Math.round(revenue * 100) / 100,
      paidAmount: Math.round(paidAmount * 100) / 100,
      avgRecordsPerDay: Math.round((records.length / days) * 10) / 10,
    };
  }

  async getStaffUtilization(clubId: string, from: Date, to: Date): Promise<StaffUtilization[]> {
    const records = await this.prisma.bookingRecord.findMany({
      where: {
        clubId,
        startAt: { gte: from, lte: to },
      },
      select: {
        staffId: true,
        status: true,
        price: true,
        staff: { select: { firstName: true, lastName: true } },
      },
    });

    const byStaff = new Map<
      string,
      {
        name: string;
        total: number;
        completed: number;
        noShow: number;
        revenue: number;
      }
    >();

    for (const r of records) {
      const existing = byStaff.get(r.staffId) ?? {
        name: `${r.staff.firstName} ${r.staff.lastName}`,
        total: 0,
        completed: 0,
        noShow: 0,
        revenue: 0,
      };
      existing.total++;
      if (r.status === 'COMPLETED') {
        existing.completed++;
        existing.revenue += Number(r.price);
      }
      if (r.status === 'NO_SHOW') existing.noShow++;
      byStaff.set(r.staffId, existing);
    }

    return Array.from(byStaff.entries()).map(([staffId, data]) => ({
      staffId,
      staffName: data.name,
      totalRecords: data.total,
      completedRecords: data.completed,
      noShowRecords: data.noShow,
      completionRate: data.total > 0 ? Math.round((data.completed / data.total) * 1000) / 10 : 0,
      totalRevenue: Math.round(data.revenue * 100) / 100,
    }));
  }

  async getPopularServices(
    clubId: string,
    from: Date,
    to: Date,
    limit = 10,
  ): Promise<PopularService[]> {
    const records = await this.prisma.bookingRecord.findMany({
      where: {
        clubId,
        startAt: { gte: from, lte: to },
      },
      select: {
        serviceId: true,
        price: true,
        service: { select: { name: true } },
      },
    });

    const byService = new Map<string, { name: string; count: number; revenue: number }>();

    for (const r of records) {
      const existing = byService.get(r.serviceId) ?? {
        name: r.service.name,
        count: 0,
        revenue: 0,
      };
      existing.count++;
      existing.revenue += Number(r.price);
      byService.set(r.serviceId, existing);
    }

    return Array.from(byService.entries())
      .map(([serviceId, data]) => ({
        serviceId,
        serviceName: data.name,
        totalBookings: data.count,
        revenue: Math.round(data.revenue * 100) / 100,
      }))
      .sort((a, b) => b.totalBookings - a.totalBookings)
      .slice(0, limit);
  }

  async getDailyTrends(clubId: string, from: Date, to: Date): Promise<DailyTrend[]> {
    const records = await this.prisma.bookingRecord.findMany({
      where: {
        clubId,
        startAt: { gte: from, lte: to },
      },
      select: {
        startAt: true,
        status: true,
        price: true,
      },
      orderBy: { startAt: 'asc' },
    });

    const byDay = new Map<string, DailyTrend>();

    for (const r of records) {
      const dateKey = r.startAt.toISOString().slice(0, 10);
      const existing = byDay.get(dateKey) ?? {
        date: dateKey,
        total: 0,
        completed: 0,
        cancelled: 0,
        noShow: 0,
        revenue: 0,
      };
      existing.total++;
      if (r.status === 'COMPLETED') {
        existing.completed++;
        existing.revenue += Number(r.price);
      }
      if (r.status === 'CANCELLED') existing.cancelled++;
      if (r.status === 'NO_SHOW') existing.noShow++;
      byDay.set(dateKey, existing);
    }

    return Array.from(byDay.values()).map((d) => ({
      ...d,
      revenue: Math.round(d.revenue * 100) / 100,
    }));
  }
}
