/**
 * Activity-based target achievement (calls, meetings, site visits).
 *
 * Month boundaries: UTC calendar month for the given (year, month) to align with
 * how period.month/year are stored on Targets. The frontend sends the viewer's
 * local calendar month; small skew at zone boundaries is a known limitation.
 *
 * QA: Complete a call/site-visit task so the linked Activity becomes completed;
 * open Team Targets for that team and month — achieved counts should match.
 */
import type { Types } from 'mongoose';
import { Activity } from '@/modules/activity/activity.model';
import { ActivityStatus } from '@/shared/constants/enum.constant';
import { TaskActivityType } from '@/modules/tasks/tasks.constant';

export type MemberActivityAchievements = {
  calls: number;
  meetings: number;
  siteVisits: number;
};

const emptyAchievements = (): MemberActivityAchievements => ({
  calls: 0,
  meetings: 0,
  siteVisits: 0,
});

/** Inclusive UTC range for calendar month (month 1–12). */
export function getUtcMonthRange(month: number, year: number): {
  start: Date;
  end: Date;
} {
  const start = new Date(Date.UTC(year, month - 1, 1, 0, 0, 0, 0));
  const end = new Date(Date.UTC(year, month, 0, 23, 59, 59, 999));
  return { start, end };
}

/**
 * Counts completed activities per member and in total, using the same buckets as
 * Lead.activityCounts / activity.service classify(): call, meeting, site-visit,
 * and schedule + task.activityType.
 */
export async function getActivityAchievementsForMembers(
  companyId: Types.ObjectId,
  memberIds: Types.ObjectId[],
  month: number,
  year: number,
): Promise<{
  byMember: Map<string, MemberActivityAchievements>;
  totals: MemberActivityAchievements;
}> {
  const uniqueIds = [
    ...new Map(memberIds.map((id) => [id.toString(), id])).values(),
  ];

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

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

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

  const rows = await Activity.aggregate<{
    _id: { member: Types.ObjectId; bucket: keyof MemberActivityAchievements };
    count: number;
  }>([
    {
      $match: {
        company: companyId,
        status: ActivityStatus.COMPLETED,
        assignedTo: { $in: uniqueIds },
        updatedAt: { $gte: start, $lte: end },
      },
    },
    {
      $lookup: {
        from: 'tasks',
        localField: 'task',
        foreignField: '_id',
        as: 'taskDoc',
      },
    },
    {
      $addFields: {
        taskActivityType: { $arrayElemAt: ['$taskDoc.activityType', 0] },
      },
    },
    {
      $addFields: {
        bucket: {
          $switch: {
            branches: [
              { case: { $eq: ['$type', 'call'] }, then: 'calls' },
              { case: { $eq: ['$type', 'meeting'] }, then: 'meetings' },
              { case: { $eq: ['$type', 'site-visit'] }, then: 'siteVisits' },
              {
                case: {
                  $and: [
                    { $eq: ['$type', 'schedule'] },
                    { $eq: ['$taskActivityType', TaskActivityType.CALL] },
                  ],
                },
                then: 'calls',
              },
              {
                case: {
                  $and: [
                    { $eq: ['$type', 'schedule'] },
                    { $eq: ['$taskActivityType', TaskActivityType.MEETING] },
                  ],
                },
                then: 'meetings',
              },
              {
                case: {
                  $and: [
                    { $eq: ['$type', 'schedule'] },
                    { $eq: ['$taskActivityType', TaskActivityType.SITE_VISIT] },
                  ],
                },
                then: 'siteVisits',
              },
            ],
            default: null,
          },
        },
      },
    },
    { $match: { bucket: { $ne: null } } },
    {
      $group: {
        _id: { member: '$assignedTo', bucket: '$bucket' },
        count: { $sum: 1 },
      },
    },
  ]);

  for (const row of rows) {
    const memberKey = row._id.member.toString();
    const bucket = row._id.bucket;
    const cur = byMember.get(memberKey) ?? emptyAchievements();
    cur[bucket] = row.count;
    byMember.set(memberKey, cur);
  }

  const totals = emptyAchievements();
  for (const m of uniqueIds) {
    const a = byMember.get(m.toString()) ?? emptyAchievements();
    totals.calls += a.calls;
    totals.meetings += a.meetings;
    totals.siteVisits += a.siteVisits;
  }

  return { byMember, totals };
}
