import { FilterQuery, Types } from 'mongoose';
import {
  IActivity,
  IActivityDoc,
  NewCreatedActivity,
  PopulatedActivity,
  QueryFilter,
  UpdateActivityBody,
} from './activity.interface';
import { Activity } from './activity.model';
import { Contact } from '@/modules/contacts/contacts.model';
import { getObjectId } from '@/shared/utils/commonHelper';
import { PaginateOptions } from '@/shared/utils/plugins/paginate/paginate';
import responseCodes from '@/shared/utils/responseCode/responseCode';
import { ApiError } from '@/shared/utils/errors';
import { defaultStatus } from '@/shared/utils/responseCode/httpStatusAlias';
import { loadEmailTemplateFromFile } from '@/shared/email/email.util';
import { updateLeadScoreFromActivity } from '../leadScore/leadScore.service';
import { sendTransactionalEmail } from '@/shared/email/email.service';
// import { sendWhatsappMessage } from '@/shared/communicationAlerts/sendMetaMsg';
// import { TriggerPoint } from '@/shared/constants/enum.constant';
import { Lead } from '../lead/lead.model';
import {
  createActivityRemainder,
  deleteActivityRemainderByActivityId,
} from './activityRemainder/activityRemainder.services';
import { ActivityStatus, Status, TriggerPoint } from '@/shared/constants/enum.constant';
import { Tasks } from '@/modules/tasks/tasks.model';
import { TaskActivityType } from '@/modules/tasks/tasks.constant';
import { sendWhatsappMessage } from '@/shared/communicationAlerts/sendMetaMsg';
import {
  buildEmailAttachments,
  buildWhatsappFilePayload,
  shouldSendEmail,
  shouldSendWhatsapp,
} from './activity.helper';
import { PropertyFile } from '../individualProperties/files';

const { ActivityResponseCodes } = responseCodes;

export const maybeUpdateLeadScoreFromActivity = async (
  activityType: string,
  leadScoreType: string,
  leadId: Types.ObjectId | string,
  activityIdToIgnore?: Types.ObjectId,
) => {
  const alreadyExists = await Activity.exists({
    type: activityType,
    lead: leadId,
    _id: { $ne: activityIdToIgnore },
  });

  if (!alreadyExists) await updateLeadScoreFromActivity(leadScoreType, leadId);
  else
    console.log(
      `Skipping lead score update for activityType "${activityType}" already exists.`,
    );
};

export const createActivity = async (
  payload: NewCreatedActivity,
): Promise<IActivityDoc> => {
  const activity = await Activity.create(payload);

  if (!activity)
    throw new ApiError(
      defaultStatus.INTERNAL_SERVER_ERROR,
      'Failed to create activity',
      true,
      '',
      ActivityResponseCodes.ACTIVITY_ERROR,
    );

  if (
    activity.status === ActivityStatus.PENDING &&
    activity.scheduleDateTime &&
    activity.type !== 'notes'
  )
    await createActivityRemainder({
      activityId: activity._id as Types.ObjectId,
      scheduleDateTime: activity.scheduleDateTime as Date,
      leadId: activity.lead as Types.ObjectId,
      contactId: activity.contact as Types.ObjectId,
      assignedTo: activity.assignedTo as Types.ObjectId,
      activityType: activity.type,
      createdBy: activity.createdBy,
      updatedBy: activity.updatedBy,
      status: activity.status,
    });

  if (
    activity.type === 'call' &&
    typeof activity.title === 'string' &&
    activity.title.toLowerCase().includes('successful')
  )
    await maybeUpdateLeadScoreFromActivity(
      'call',
      'phone_conversation',
      activity.lead,
      activity._id as Types.ObjectId,
    );

  if (activity.type === 'book')
    await maybeUpdateLeadScoreFromActivity(
      'book',
      'booking_initiated',
      activity.lead,
      activity._id as Types.ObjectId,
    );

  if (['files', 'quote'].includes(payload.type)) {
    const fullActivity = await Activity.findById(activity._id)
      .populate([
        { path: 'unit', select: 'unitNumber price' },
        { path: 'files' },
        {
          path: 'lead',
          populate: [
            { path: 'contact', select: '_id email workEmail secondaryEmail' },
            { path: 'project', select: 'projectName' },
          ]
        },
        { path: 'company', select: 'name _id' },
        {
          path: 'assignedTo',
          select: 'firstName lastName  phone',
        },
      ])
      .lean<PopulatedActivity>();

    const fileIds = payload.files ? payload.files.map(file => getObjectId(file)) : fullActivity.files ? fullActivity.files.map(file => getObjectId(String(file))) : [];
    const files = await PropertyFile.find({ _id: { $in: fileIds }, status: Status.ACTIVE }).select('name fileUrl fileType');

    if (!fullActivity) return activity;

    const contact = fullActivity.lead?.contact;

    const toEmail =
      (typeof payload.primaryEmail === 'string' && payload.primaryEmail.trim()) ||
      contact?.email?.trim() ||
      '';
    if (!toEmail) {
      console.warn('No recipient email (primary or contact email)');
      return activity;
    }

    const ccRaw =
      (typeof payload.secondaryEmail === 'string' && payload.secondaryEmail.trim()) ||
      contact?.secondaryEmail?.trim() ||
      contact?.workEmail?.trim() ||
      '';
    const ccEmail =
      ccRaw && ccRaw.toLowerCase() !== toEmail.toLowerCase() ? ccRaw : undefined;

    const subject = `📩 New ${payload.type === 'quote' ? 'Quote' : 'Files'} Activity - ${fullActivity.title}`;
    
    const body = loadEmailTemplateFromFile(
      payload.type === 'quote' ? 'activityQuoteEmail' : 'activityFilesEmail',
      {
        title: fullActivity.title,
        project: fullActivity.lead?.project?.projectName || 'N/A',
        comment: fullActivity.comment || 'No comment',
        unitNumber: fullActivity.unit?.unitNumber || 'N/A',
        finalAmount: fullActivity.unit?.price || undefined
      },
    );

    const attachments = buildEmailAttachments(payload, files, fullActivity.quotationFile);

    if (shouldSendEmail(payload))
      await sendTransactionalEmail({
        to: toEmail,
        cc: ccEmail,
        subject,
        htmlContent: body,
        attachments,
      });


    if (shouldSendWhatsapp(payload)) {
      const whatsappFilesPayload = buildWhatsappFilePayload(
        payload,
        fullActivity,
      );

      const whatsappVariables = [
        fullActivity?.lead?.contactDetails?.name || 'N/A',
        fullActivity?.company?.name || 'N/A',
        fullActivity?.lead?.project?.projectName || 'N/A',
        whatsappFilesPayload,
        `${fullActivity.assignedTo?.firstName || ''} ${fullActivity.assignedTo?.lastName || ''
        }`,
        `${fullActivity.assignedTo?.phone?.countryCode || ''} ${fullActivity.assignedTo?.phone?.number || ''
        }`,
        fullActivity.company?.name || 'N/A',
      ];

      await sendWhatsappMessage({
        companyId: fullActivity.company!._id,
        triggerPoint: TriggerPoint['WhenFilesSharedFromCRM'],
        toNumber: fullActivity?.lead?.contactDetails?.phone || 'N/A',
        variables: whatsappVariables,
      });
    }

  }

  if (activity.type === 'files')
    await maybeUpdateLeadScoreFromActivity(
      'files',
      'property_specs',
      activity.lead,
      activity._id as Types.ObjectId,
    );
  else if (activity.type === 'quote')
    await maybeUpdateLeadScoreFromActivity(
      'quote',
      'quotation_shared',
      activity.lead,
      activity._id as Types.ObjectId,
    );

  return activity;
};

export const getActivityById = async (
  id: string,
): Promise<IActivityDoc | null> => {
  const activity = await Activity.findById(id);
  if (!activity)
    throw new ApiError(
      defaultStatus.NOT_FOUND,
      'Activity not found',
      true,
      '',
      ActivityResponseCodes.ACTIVITY_NOT_FOUND,
    );

  return activity;
};

export const updateActivity = async (
  id: string,
  updateData: UpdateActivityBody,
): Promise<void> => {
  const before = await Activity.findById(getObjectId(id))
    .select('status type lead task')
    .lean();
  if (!before)
    throw new ApiError(
      defaultStatus.NOT_FOUND,
      'Activity not found',
      false,
      '',
      ActivityResponseCodes.ACTIVITY_NOT_FOUND,
    );

  const result = await Activity.updateOne(
    { _id: getObjectId(id) },
    { $set: updateData },
  );
  if (result.matchedCount === 0)
    throw new ApiError(
      defaultStatus.NOT_FOUND,
      'Activity not found',
      false,
      '',
      ActivityResponseCodes.ACTIVITY_NOT_FOUND,
    );

  const afterStatus = (updateData as any)?.status ?? before.status;
  const beforeStatus = before.status;

  const classify = async (): Promise<
    'callAttempts' | 'meetings' | 'siteVisits' | null
  > => {
    if (before.type === 'call') return 'callAttempts';
    if (before.type === 'meeting') return 'meetings';
    if (before.type === 'site-visit') return 'siteVisits';

    if ((before.type as string) === 'schedule' && before.task) {
      const task = await Tasks.findById(before.task)
        .select('activityType')
        .lean();
      if (task?.activityType === TaskActivityType.MEETING) return 'meetings';
      if (task?.activityType === TaskActivityType.SITE_VISIT)
        return 'siteVisits';
      if (task?.activityType === TaskActivityType.CALL) return 'callAttempts';
    }

    return null;
  };

  // Update lead counters only on transition to/from completed
  if (before.lead) {
    const bucket = await classify();
    if (bucket) {
      if (
        beforeStatus !== ActivityStatus.COMPLETED &&
        afterStatus === ActivityStatus.COMPLETED
      ) {
        await Lead.updateOne(
          { _id: before.lead },
          {
            $inc: { [`activityCounts.${bucket}`]: 1, 'activityCounts.totalActivity': 1 },
            $set: { activityCountsUpdatedAt: new Date() },
          },
        );
      } else if (
        beforeStatus === ActivityStatus.COMPLETED &&
        afterStatus !== ActivityStatus.COMPLETED
      ) {
        await Lead.updateOne(
          { _id: before.lead },
          {
            $inc: { [`activityCounts.${bucket}`]: -1, 'activityCounts.totalActivity': -1 },
            $set: { activityCountsUpdatedAt: new Date() },
          },
        );
      } else {
      }
    }
  }

  if (updateData.status === 'completed')
    await deleteActivityRemainderByActivityId(id);
};

export const deleteActivity = async (id: string): Promise<void> => {
  const deleted = await Activity.findByIdAndDelete(getObjectId(id))
    .select('status type lead task')
    .lean();
  if (!deleted)
    throw new ApiError(
      defaultStatus.NOT_FOUND,
      'Activity not found',
      false,
      '',
      ActivityResponseCodes.ACTIVITY_NOT_FOUND,
    );

  // If deleting a completed activity, decrement the corresponding counter.
  if (deleted.lead && deleted.status === ActivityStatus.COMPLETED) {
    let bucket: 'callAttempts' | 'meetings' | 'siteVisits' | null = null;
    if (deleted.type === 'call') bucket = 'callAttempts';
    else if (deleted.type === 'meeting') bucket = 'meetings';
    else if (deleted.type === 'site-visit') bucket = 'siteVisits';
    else if ((deleted.type as string) === 'schedule' && deleted.task) {
      const task = await Tasks.findById(deleted.task)
        .select('activityType')
        .lean();
      if (task?.activityType === TaskActivityType.MEETING) bucket = 'meetings';
      else if (task?.activityType === TaskActivityType.SITE_VISIT)
        bucket = 'siteVisits';
      else if (task?.activityType === TaskActivityType.CALL)
        bucket = 'callAttempts';
    }

    if (bucket) {
      await Lead.updateOne(
        { _id: deleted.lead },
        {
          $inc: { [`activityCounts.${bucket}`]: -1, 'activityCounts.totalActivity': -1 },
          $set: { activityCountsUpdatedAt: new Date() },
        },
      );

      await Lead.updateOne(
        {
          _id: deleted.lead,
          [`activityCounts.${bucket}`]: { $lt: 0 },
        },
        { $set: { [`activityCounts.${bucket}`]: 0 } },
      );
    }
  }
};

export const queryActivity = async (
  rawFilter: QueryFilter,
  options: PaginateOptions,
) => {
  const {
    search,
    lead,
    task,
    assignedTo,
    status,
    contact,
    company,
    ...baseFilters
  } = rawFilter;

  const merged = {
    ...baseFilters,
    ...(contact && { contact: getObjectId(contact) }),
    ...(task && { task: getObjectId(task) }),
    ...(assignedTo && { assignedTo: getObjectId(assignedTo) }),
    ...(status && { status }),
    ...(company && { company: getObjectId(company) }),
  };

  const filter: FilterQuery<IActivity> = Object.fromEntries(
    Object.entries(merged).filter(([, v]) => v != null),
  );

  if (search && typeof search === 'string')
    filter.$or = [
      { title: { $regex: search, $options: 'i' } },
      { comment: { $regex: search, $options: 'i' } },
    ];

  if (lead) {
    const leadDoc = await Lead.findById(lead).select('contact');

    if (leadDoc?.contact) {
      const contactId = getObjectId(leadDoc.contact);

      filter.$or = [
        { lead: getObjectId(lead) },

        {
          contact: contactId,
          title: { $in: ['Contact Created', 'Contact Updated'] },
        },
        ...(filter.$or || []),
      ];
    } else {
      filter.lead = getObjectId(lead);
    }
  }

  if (options.populate) {
    const defaultPopulate = 'task:_id,siteVisitImage';
    options.populate = `${defaultPopulate};${options.populate}`;
  } else {
    options.populate = 'task:_id,siteVisitImage';
  }

  return Activity.paginate(filter, options);
};
