import { Types, FilterQuery } from 'mongoose';

import {
  ILeadScoreConfigDoc,
  NewCreatedLeadScoreConfig,
  QueryFilter,
  UpdateLeadScoreConfigBody,
} from './leadScore.interface';
import { LeadScoreConfig } from './leadScore.model';
import responseCodes from '@/shared/utils/responseCode/responseCode';
import { ApiError } from '@/shared/utils/errors';
import { defaultStatus } from '@/shared/utils/responseCode/httpStatusAlias';
import { PaginateOptions } from '@/shared/utils/plugins/paginate/paginate';
import { getObjectId } from '@/shared/utils/commonHelper';
import { defaultActivityPoints } from './leadScore.constant';
import { Lead } from '../lead/lead.model';

const { LeadScoreConfigResponseCodes } = responseCodes;

export const createLeadScoreConfig = async (
  payload: NewCreatedLeadScoreConfig,
): Promise<ILeadScoreConfigDoc> => {
  const existing = await LeadScoreConfig.findOne({
    companyId: getObjectId(payload.companyId),
  });

  if (existing)
    throw new ApiError(
      defaultStatus.OK,
      'Lead Score Config already exists for this company',
      true,
      '',
      LeadScoreConfigResponseCodes.CONFIG_ALREADY_EXISTS,
    );

  const config = await LeadScoreConfig.create(payload);

  if (!config)
    throw new ApiError(
      defaultStatus.OK,
      'Failed to create Lead Score Config',
      true,
      '',
      LeadScoreConfigResponseCodes.CONFIG_ERROR,
    );

  return config;
};

export const getLeadScoreConfigById = async (
  id: string,
): Promise<ILeadScoreConfigDoc | null> => {
  const config = await LeadScoreConfig.findById(id);

  if (!config)
    throw new ApiError(
      defaultStatus.OK,
      'Lead Score Config not found',
      true,
      '',
      LeadScoreConfigResponseCodes.CONFIG_NOT_FOUND,
    );

  return config;
};

export const updateLeadScoreConfig = async (
  id: string,
  updateData: UpdateLeadScoreConfigBody,
): Promise<ILeadScoreConfigDoc | null> => {
  const config = await LeadScoreConfig.findByIdAndUpdate(
    getObjectId(id),
    { $set: updateData },
    { new: true },
  );

  if (!config)
    throw new ApiError(
      defaultStatus.OK,
      'Lead Score Config not found',
      true,
      '',
      LeadScoreConfigResponseCodes.CONFIG_NOT_FOUND,
    );

  return config;
};

export const queryLeadScoreConfigs = async (
  rawFilter: QueryFilter,
  options: PaginateOptions,
) => {
  const { companyId } = rawFilter;

  const filter: FilterQuery<ILeadScoreConfigDoc> = {
    ...(companyId && { companyId: getObjectId(companyId) }),
  };

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

export const deleteLeadScoreConfig = async (id: string): Promise<void> => {
  const deleted = await LeadScoreConfig.findByIdAndDelete(getObjectId(id));

  if (!deleted)
    throw new ApiError(
      defaultStatus.NOT_FOUND,
      'Lead Score Config not found',
      false,
      '',
      LeadScoreConfigResponseCodes.CONFIG_NOT_FOUND,
    );
};

export const createDefaultLeadScoreConfig = async (
  companyId: Types.ObjectId,
  createdBy?: Types.ObjectId,
): Promise<ILeadScoreConfigDoc> =>
  LeadScoreConfig.create({
    companyId,
    activityPoints: defaultActivityPoints,
    createdBy,
    updatedBy: createdBy,
    zeroScoreIfLeadLost: false,
    fullScoreIfLeadWon: false,
  });

/**
 * @param activityId
 * @param leadId
 */
export const updateLeadScoreFromActivity = async (
  activityId: string,
  leadId: Types.ObjectId | string,
): Promise<void> => {
  // Step 1: Find the lead and its company
  const lead = await Lead.findById(leadId).lean();
  if (!lead) throw new Error('Lead not found');
  if (!lead.company) throw new Error('Lead does not have a companyId');

  // Step 2: Find the company's lead score config
  const leadScoreConfig = await LeadScoreConfig.findOne({
    companyId: lead.company,
  }).lean();
  if (!leadScoreConfig)
    throw new Error('LeadScoreConfig not found for the company');

  // Step 3: Get points for the given activityId
  const activityPoint = leadScoreConfig.activityPoints.find(
    (point) => point.activityId === activityId,
  );

  if (!activityPoint) {
    console.warn(`No points configured for activityId: ${activityId}`);
    return;
  }

  // Step 4: Atomically increment leadScore in DB
  await Lead.findByIdAndUpdate(leadId, {
    $inc: { leadScore: activityPoint.points },
  });
};
