import { Request, Response } from 'express';
import Appointments from '../../appointments/schema/appointments.schema';
import { AppointmentStatus } from '../../appointments/enums/appointments.enum';
import { StockMovement } from '../../products/schema/product.schema';

const BOOKING_STATUSES = Object.values(AppointmentStatus);

function parseDateKey(value: string) {
  return value?.slice(0, 10);
}

function createdAtRange(from: string, to: string) {
  const range: Record<string, Date> = {};
  if (from) range.$gte = new Date(`${from}T00:00:00.000Z`);
  if (to) range.$lte = new Date(`${to}T23:59:59.999Z`);
  return Object.keys(range).length ? range : null;
}

function productUnitPrice(product: any) {
  return product?.sellingPrice || product?.price || 0;
}

function dayKeyFromDate(value: Date | string) {
  return new Date(value).toISOString().slice(0, 10);
}

export class AnalyticsController {
  static async dashboard(req: Request, res: Response) {
    try {
      const from = parseDateKey(String(req.query.from || ''));
      const to = parseDateKey(String(req.query.to || ''));

      const appointmentFilter: Record<string, unknown> = {};
      if (from || to) {
        appointmentFilter.date = {};
        if (from) (appointmentFilter.date as Record<string, string>).$gte = from;
        if (to) (appointmentFilter.date as Record<string, string>).$lte = to;
      }

      const movementFilter: Record<string, unknown> = { type: 'OUT' };
      const createdRange = createdAtRange(from, to);
      if (createdRange) movementFilter.createdAt = createdRange;

      const [appointments, outMovements] = await Promise.all([
        Appointments.find(appointmentFilter).sort({ date: 1 }),
        StockMovement.find(movementFilter).sort({ createdAt: -1 }).populate('productId'),
      ]);

      const byStatus: Record<string, number> = {};
      const bookingsByDay: Record<string, number> = {};
      const bookingRevenueByDay: Record<string, number> = {};
      const byPayment: Record<string, number> = {};
      const byService: Record<string, { count: number; revenue: number }> = {};
      const byPaymentMethod: Record<string, number> = {};

      appointments.forEach((a: any) => {
        byStatus[a.status] = (byStatus[a.status] || 0) + 1;
        bookingsByDay[a.date] = (bookingsByDay[a.date] || 0) + 1;
        bookingRevenueByDay[a.date] = (bookingRevenueByDay[a.date] || 0) + (a.amountPaid || 0);
        byPayment[a.paymentStatus || 'UNPAID'] = (byPayment[a.paymentStatus || 'UNPAID'] || 0) + 1;
        byPaymentMethod[a.paymentMethod || 'NONE'] = (byPaymentMethod[a.paymentMethod || 'NONE'] || 0) + 1;

        const serviceKey = a.service || 'Unknown';
        if (!byService[serviceKey]) byService[serviceKey] = { count: 0, revenue: 0 };
        byService[serviceKey].count += 1;
        byService[serviceKey].revenue += a.amountPaid || 0;
      });

      const productByName: Record<string, { productId: string; name: string; quantity: number; revenue: number }> = {};
      const productRevenueByDay: Record<string, number> = {};
      const productUnitsByDay: Record<string, number> = {};
      let productRevenue = 0;
      let productUnitsSold = 0;

      outMovements.forEach((m: any) => {
        const product = m.productId;
        const unitPrice = productUnitPrice(product);
        const lineRevenue = (m.quantity || 0) * unitPrice;
        const day = dayKeyFromDate(m.createdAt);

        productRevenue += lineRevenue;
        productUnitsSold += m.quantity || 0;
        productRevenueByDay[day] = (productRevenueByDay[day] || 0) + lineRevenue;
        productUnitsByDay[day] = (productUnitsByDay[day] || 0) + (m.quantity || 0);

        const key = product?._id?.toString() || 'unknown';
        if (!productByName[key]) {
          productByName[key] = {
            productId: key,
            name: product?.name || 'Unknown product',
            quantity: 0,
            revenue: 0,
          };
        }
        productByName[key].quantity += m.quantity || 0;
        productByName[key].revenue += lineRevenue;
      });

      const bookingRevenue = appointments.reduce((sum: number, a: any) => sum + (a.amountPaid || 0), 0);
      const bookingDue = appointments.reduce((sum: number, a: any) => sum + (a.amountDue || 0), 0);
      const paidBookings = appointments.filter((a: any) => a.paymentStatus === 'PAID').length;
      const todayKey = new Date().toISOString().slice(0, 10);

      const allDays = new Set([
        ...Object.keys(bookingsByDay),
        ...Object.keys(bookingRevenueByDay),
        ...Object.keys(productRevenueByDay),
      ]);

      const revenueByDay = Array.from(allDays)
        .sort()
        .map((date) => ({
          date,
          bookingRevenue: bookingRevenueByDay[date] || 0,
          productRevenue: productRevenueByDay[date] || 0,
          totalRevenue: (bookingRevenueByDay[date] || 0) + (productRevenueByDay[date] || 0),
          bookings: bookingsByDay[date] || 0,
          unitsSold: productUnitsByDay[date] || 0,
        }));

      const upcoming = appointments
        .filter((a: any) => a.date >= todayKey && a.status !== 'CANCELLED')
        .slice(0, 5)
        .map((a: any) => ({
          id: a._id,
          fullName: a.fullName,
          service: a.service,
          date: a.date,
          time: a.time,
          status: a.status,
          amountPaid: a.amountPaid || 0,
        }));

      const topProducts = Object.values(productByName)
        .sort((a, b) => b.revenue - a.revenue)
        .slice(0, 6);

      const topServices = Object.entries(byService)
        .map(([service, data]) => ({ service, ...data }))
        .sort((a, b) => b.revenue - a.revenue)
        .slice(0, 6);

      const activeBookings = appointments.filter((a: any) => a.status !== AppointmentStatus.CANCELLED);
      const completionRate = activeBookings.length
        ? Math.round(((byStatus.COMPLETED || 0) / activeBookings.length) * 100)
        : 0;

      const byStatusList = BOOKING_STATUSES.map((status) => ({
        status,
        count: byStatus[status] || 0,
      }));

      return res.status(200).json({
        success: true,
        response: {
          range: { from: from || null, to: to || null },
          summary: {
            totalRevenue: bookingRevenue + productRevenue,
            bookingRevenue,
            productRevenue,
            bookingDue,
            totalBookings: appointments.length,
            productUnitsSold,
            productTransactions: outMovements.length,
            paidBookings,
            completionRate,
            todayAppointments: appointments.filter((a: any) => a.date === todayKey).length,
          },
          bookings: {
            total: appointments.length,
            pending: byStatus.PENDING || 0,
            confirmed: byStatus.CONFIRMED || 0,
            completed: byStatus.COMPLETED || 0,
            cancelled: byStatus.CANCELLED || 0,
            paid: byPayment.PAID || 0,
            unpaid: byPayment.UNPAID || 0,
            revenue: bookingRevenue,
            due: bookingDue,
            averagePaid: paidBookings ? Math.round(bookingRevenue / paidBookings) : 0,
            byDay: Object.entries(bookingsByDay).map(([date, count]) => ({ date, count })),
            byStatus: byStatusList,
            byPayment: Object.entries(byPayment).map(([status, count]) => ({ status, count })),
            byPaymentMethod: Object.entries(byPaymentMethod).map(([method, count]) => ({ method, count })),
            topServices,
            upcoming,
          },
          products: {
            revenue: productRevenue,
            unitsSold: productUnitsSold,
            transactions: outMovements.length,
            averageSale: outMovements.length ? Math.round(productRevenue / outMovements.length) : 0,
            byProduct: topProducts,
            revenueByDay: Object.entries(productRevenueByDay).map(([date, revenue]) => ({
              date,
              revenue,
              units: productUnitsByDay[date] || 0,
            })),
          },
          revenueByDay,
          // legacy fields for backward compatibility
          total: appointments.length,
          pending: byStatus.PENDING || 0,
          confirmed: byStatus.CONFIRMED || 0,
          completed: byStatus.COMPLETED || 0,
          cancelled: byStatus.CANCELLED || 0,
          today: appointments.filter((a: any) => a.date === todayKey).length,
          paid: byPayment.PAID || 0,
          revenue: bookingRevenue,
          byDay: Object.entries(bookingsByDay).map(([date, count]) => ({ date, count })),
          byStatus: byStatusList,
        },
      });
    } catch (error) {
      console.error('Analytics dashboard error:', error);
      return res.status(500).json({ success: false, message: 'System error' });
    }
  }
}
