import type { Types } from 'mongoose';
import { Customer } from '@/modules/customer/customer.model';
import { UnitBookingOrHold } from '@/modules/activity/unitBookingOrHold/unitBookingOrHold.model';
import { getUtcMonthRange } from '@/modules/targets/targetActivityAchievements';

export type MemberSalesAchievements = {
  unitsSold: number;
  salesAmount: number;
};

const emptySales = (): MemberSalesAchievements => ({
  unitsSold: 0,
  salesAmount: 0,
});

function addToMap(
  map: Map<string, MemberSalesAchievements>,
  key: string,
  units: number,
  amount: number,
) {
  const cur = map.get(key) ?? emptySales();
  map.set(key, {
    unitsSold: cur.unitsSold + units,
    salesAmount: cur.salesAmount + amount,
  });
}

/**
 * Per soldBy: booked units in period and sum of booking amounts.
 */
export async function getSalesAchievementsForMembers(
  companyId: Types.ObjectId,
  memberIds: Types.ObjectId[],
  month: number,
  year: number,
): Promise<{
  byMember: Map<string, MemberSalesAchievements>;
  totals: MemberSalesAchievements;
}> {
  const uniqueIds = [
    ...new Map(memberIds.map((id) => [id.toString(), id])).values(),
  ];

  const byMember = new Map<string, MemberSalesAchievements>();
  for (const id of uniqueIds) {
    byMember.set(id.toString(), emptySales());
  }

  if (uniqueIds.length === 0) {
    return { byMember, totals: emptySales() };
  }

  const { start, end } = getUtcMonthRange(month, year);

  const [bookingRows, customerRows] = await Promise.all([
    UnitBookingOrHold.aggregate<{
      _id: Types.ObjectId;
      unitsSold: number;
      salesAmount: number;
    }>([
      {
        $match: {
          action: 'book',
          soldBy: { $in: uniqueIds },
          createdAt: { $gte: start, $lte: end },
        },
      },
      {
        $lookup: {
          from: 'leads',
          localField: 'lead',
          foreignField: '_id',
          as: 'leadDoc',
        },
      },
      { $unwind: { path: '$leadDoc', preserveNullAndEmptyArrays: true } },
      {
        $match: {
          'leadDoc.company': companyId,
        },
      },
      {
        $lookup: {
          from: 'units',
          localField: 'unit',
          foreignField: '_id',
          as: 'unitDoc',
        },
      },
      {
        $lookup: {
          from: 'individualproperties',
          localField: 'property',
          foreignField: '_id',
          as: 'propertyDoc',
        },
      },
      {
        $addFields: {
          unitPrice: { $arrayElemAt: ['$unitDoc.price', 0] },
          propertyPrice: {
            $ifNull: [
              { $arrayElemAt: ['$propertyDoc.totalPrice', 0] },
              { $arrayElemAt: ['$propertyDoc.price', 0] },
            ],
          },
        },
      },
      {
        $addFields: {
          sellPrice: {
            $ifNull: ['$unitPrice', '$propertyPrice'],
          },
        },
      },
      {
        $group: {
          _id: '$soldBy',
          unitsSold: { $sum: 1 },
          salesAmount: {
            $sum: { $ifNull: ['$sellPrice', 0] },
          },
        },
      },
    ]),
    Customer.aggregate<{
      _id: Types.ObjectId;
      unitsSold: number;
      salesAmount: number;
    }>([
      {
        $match: {
          $and: [
            { company: companyId },
            {
              $or: [
                { bookingStatus: { $exists: false } },
                { bookingStatus: { $ne: 'cancelled' } },
              ],
            },
            {
              $or: [
                { unitBookingOrHold: { $exists: false } },
                { unitBookingOrHold: null },
              ],
            },
          ],
        },
      },
      { $unwind: '$sales' },
      { $match: { 'sales.soldBy': { $in: uniqueIds } } },
      {
        $addFields: {
          _saleAt: { $ifNull: ['$sales.salesAt', '$createdAt'] },
        },
      },
      {
        $match: {
          _saleAt: { $gte: start, $lte: end },
        },
      },
      {
        $lookup: {
          from: 'units',
          localField: 'sales.unit',
          foreignField: '_id',
          as: 'unitDoc',
        },
      },
      {
        $lookup: {
          from: 'individualproperties',
          localField: 'sales.property',
          foreignField: '_id',
          as: 'propertyDoc',
        },
      },
      {
        $addFields: {
          unitPrice: { $arrayElemAt: ['$unitDoc.price', 0] },
          propertyPrice: {
            $ifNull: [
              { $arrayElemAt: ['$propertyDoc.totalPrice', 0] },
              { $arrayElemAt: ['$propertyDoc.price', 0] },
            ],
          },
        },
      },
      {
        $addFields: {
          sellPrice: {
            $ifNull: ['$unitPrice', '$propertyPrice'],
          },
        },
      },
      {
        $group: {
          _id: '$sales.soldBy',
          unitsSold: { $sum: 1 },
          salesAmount: {
            $sum: { $ifNull: ['$sellPrice', 0] },
          },
        },
      },
    ]),
  ]);

  for (const row of bookingRows) {
    addToMap(byMember, row._id.toString(), row.unitsSold, row.salesAmount);
  }
  for (const row of customerRows) {
    addToMap(byMember, row._id.toString(), row.unitsSold, row.salesAmount);
  }

  const totals = emptySales();
  for (const m of uniqueIds) {
    const s = byMember.get(m.toString()) ?? emptySales();
    totals.unitsSold += s.unitsSold;
    totals.salesAmount += s.salesAmount;
  }

  return { byMember, totals };
}

const monthYearKey = (year: number, month: number) => `${year}-${month}`;

/**
 * Real booking/customer sales totals per calendar month (for target trend charts).
 */
export async function getSalesAchievementsByCalendarMonths(
  companyId: Types.ObjectId,
  memberIds: Types.ObjectId[],
  months: readonly { month: number; year: number }[],
): Promise<Map<string, MemberSalesAchievements>> {
  const map = new Map<string, MemberSalesAchievements>();
  if (months.length === 0) return map;

  const rows = await Promise.all(
    months.map(async (m) => {
      const { totals } = await getSalesAchievementsForMembers(
        companyId,
        memberIds,
        m.month,
        m.year,
      );
      return { key: monthYearKey(m.year, m.month), totals };
    }),
  );

  for (const { key, totals } of rows) {
    map.set(key, totals);
  }
  return map;
}
