import {
  IContactDoc,
  NewCreatedContact,
  QueryFilter,
  UpdateContactBody,
  FindOrCreateByPhoneParams,
} from '@/modules/contacts/contacts.interface';
import { Contact } from '@/modules/contacts/contacts.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 {
  buildAggregationStats,
  buildContactFilter,
} from '@/modules/contacts/contacts.helper';
import { getDateRanges } from '@/shared/utils/date';
import { validateCustomFields } from '@/shared/utils/customFieldValidation';
import { CustomFormNames } from '../customFields/customFields.constant';
import {
  deleteWithReferences,
  safeDeleteById,
} from '@/shared/utils/guard/ref-guard';
import { Types } from 'mongoose';
import { Company } from '../company/company.model';

const { ContactResponseCodes } = responseCodes;

export const createContact = async (
  data: NewCreatedContact,
): Promise<IContactDoc> => {
  let normalizedEmail: string | undefined;

  const existsPhoneInCompany = await Contact.findOne({
    company: data.company,
    phone: {
      $elemMatch: {
        $or: data.phone.map((ph) => ({
          countryCode: ph.countryCode,
          number: ph.number,
        })),
      },
    },
  });

  if (existsPhoneInCompany)
    throw new ApiError(
      defaultStatus.BAD_REQUEST,
      'Contact with this phone already exists',
      true,
      '',
      ContactResponseCodes.CONTACT_ALREADY_EXISTS,
    );

  // normalize only if provided
  if (data.email && data.email.trim() !== '') {
    normalizedEmail = data.email.toLowerCase();

    // check duplicates only if email exists
    const existing = await Contact.findOne({ email: normalizedEmail });
    if (existing && existing?.source?.toString() === data?.source?.toString())
      throw new ApiError(
        defaultStatus.BAD_REQUEST,
        'Contact with this email and source already exists',
        true,
        '',
        ContactResponseCodes.CONTACT_ALREADY_EXISTS,
        existing,
      );
  }

  // validate custom fields
  const customFields = await validateCustomFields({
    customFields: data.customFields,
    companyId: data.company,
    formName: CustomFormNames.CONTACT,
    errorCode: ContactResponseCodes.CONTACT_INVALID_CUSTOM_FIELDS,
  });

  // create contact
  const contact = await Contact.create({
    ...data,
    ...(normalizedEmail && { email: normalizedEmail }), // only set if present
    ...(customFields && { customFields }),
  });

  return contact;
};

export const getContactById = async (
  id: string,
): Promise<IContactDoc | null> => {
  const contact = await Contact.findById(id).populate([
    {
      path: 'source country state city createdBy',
      select: 'name firstName lastName',
    },
    {
      path: 'shortlisted',
      select:
        'id title ownerName ownerContact price monthlyRent status availability createdBy',
      populate: [
        { path: 'city', select: 'name' },
        { path: 'state', select: 'name' },
        { path: 'locality', select: 'name' },
        { path: 'configuration', select: 'name' },
        { path: 'subcategory', select: 'name' },
        { path: 'createdBy', select: 'firstName lastName' },
      ],
    },
    {
      path: 'removed',
      select:
        'id title ownerName ownerContact price monthlyRent status availability createdBy',
      populate: [
        { path: 'city', select: 'name' },
        { path: 'state', select: 'name' },
        { path: 'locality', select: 'name' },
        { path: 'configuration', select: 'name' },
        { path: 'subcategory', select: 'name' },
        { path: 'createdBy', select: 'firstName lastName' },
      ],
    },
  ]);

  if (!contact)
    throw new ApiError(
      defaultStatus.OK,
      'Contact not found',
      true,
      '',
      ContactResponseCodes.CONTACT_NOT_FOUND,
    );

  return contact;
};

export const updateContact = async (
  id: string,
  updateData: UpdateContactBody,
): Promise<boolean | null> => {
  try {
    const existingContact = await Contact.findById(id).lean();
    if (!existingContact)
      throw new ApiError(
        defaultStatus.OK,
        'Contact not found',
        true,
        '',
        ContactResponseCodes.CONTACT_NOT_FOUND,
      );

    const { customFields, ...rest } = updateData;

    let validatedCustomFields: Record<string, unknown> = {};
    if (customFields && Object.keys(customFields).length > 0)
      validatedCustomFields = await validateCustomFields({
        customFields,
        companyId: existingContact.company,
        formName: CustomFormNames.CONTACT,
        errorCode: ContactResponseCodes.CONTACT_INVALID_CUSTOM_FIELDS,
      });

    const result = await Contact.findByIdAndUpdate(
      id,
      {
        ...rest,
        ...(Object.keys(validatedCustomFields).length > 0 && {
          customFields: validatedCustomFields,
        }),
      },
      { new: true },
    );

    if (!result)
      throw new ApiError(
        defaultStatus.OK,
        'Contact not found',
        true,
        '',
        ContactResponseCodes.CONTACT_NOT_FOUND,
      );

    return true;
  } catch (err) {
    if (err instanceof ApiError) throw err;
    console.log(err);

    throw new ApiError(
      defaultStatus.OK,
      'Failed to update contact',
      true,
      '',
      ContactResponseCodes.CONTACT_ERROR,
    );
  }
};

export const queryContacts = async (
  rawFilter: QueryFilter,
  options: PaginateOptions,
) => {
  try {
    const filter = buildContactFilter(rawFilter);
    const contacts = await Contact.paginate(filter, options);

    const dateRanges = getDateRanges();

    const matchBase = rawFilter.companyId
      ? { company: getObjectId(rawFilter.companyId) }
      : {};

    const stats = await buildAggregationStats(Contact, matchBase, dateRanges);

    return { ...contacts, stats };
  } catch (_error) {
    throw new ApiError(
      defaultStatus.INTERNAL_SERVER_ERROR,
      'Failed to query contacts',
      true,
      '',
      ContactResponseCodes.CONTACT_ERROR,
    );
  }
};

export const deleteContact = async (id: string): Promise<boolean | null> => {
  try {
    // await safeDeleteById(Contact, id, ContactResponseCodes.CONTACT_IN_USE);
    await deleteWithReferences(Contact, id);
    return true;
  } catch (error) {
    if (error instanceof ApiError) throw error;
    throw new ApiError(
      defaultStatus.OK,
      'Failed to delete contact',
      true,
      '',
      ContactResponseCodes.COMPANY_ERROR,
    );
  }
};

export const findOrCreateByPhone = async ({
  firstName,
  lastName,
  phone,
  secondaryPhone,
  email,
  secondaryEmail,
  company,
  createdBy,
  source,
}: FindOrCreateByPhoneParams) => {
  const primaryNum = Number(phone);

  // check existing (match legacy docs whether number was stored as string or number)
  const existing = await Contact.findOne({
    company,
    $or: [{ 'phone.number': primaryNum }, { 'phone.number': phone }],
  });

  if (existing) return existing;

  // fetch company details to populate location fields
  const companyDoc = await Company.findById(company).select(
    'country state city pincode',
  );

  if (!companyDoc) throw new Error('Company not found');

  const phoneList = [
    {
      countryCode: 91,
      number: primaryNum,
      isPrimary: true,
    },
  ];

  const sec = secondaryPhone?.trim();
  if (sec && sec !== phone) {
    phoneList.push({
      countryCode: 91,
      number: Number(sec),
      isPrimary: false,
    });
  }

  const createPayload: Record<string, unknown> = {
    firstName,
    lastName,
    phone: phoneList,
    company,
    country: companyDoc.country,
    state: companyDoc.state,
    city: companyDoc.city,
    pinCode: companyDoc.pincode,
  };

  if (createdBy) createPayload.createdBy = createdBy;
  if (source) createPayload.source = source;

  const emailTrimmed = email?.trim();
  if (emailTrimmed) createPayload.email = emailTrimmed;

  const secondaryEmailTrimmed = secondaryEmail?.trim();
  if (secondaryEmailTrimmed) createPayload.workEmail = secondaryEmailTrimmed;

  const newContact = await Contact.create(createPayload);

  return newContact;
};

export const checkIfRefContact = async (id: string) => {
  try {
    await safeDeleteById(Contact, id, ContactResponseCodes.CONTACT_IN_USE);
  } catch (error) {
    if (error instanceof ApiError) throw error;
    throw new ApiError(
      defaultStatus.OK,
      'Failed to check contact protection',
      true,
      '',
      ContactResponseCodes.CONTACT_ERROR,
    );
  }
};
