import mongoose, { PipelineStage, Types } from 'mongoose';

import { ApiError } from '@/shared/utils/errors';
import { defaultStatus } from '@/shared/utils/responseCode/httpStatusAlias';
import { Customer } from '@/modules/customer/customer.model';
import {
  NewCreatedCustomer,
  PopulatedCustomer,
  UpdateCustomerBody,
} from '@/modules/customer/customer.interface';
import responseCodes from '@/shared/utils/responseCode/responseCode';

import User from '@/modules/user/user.model';
import { getEntityByIdWithQueryString } from '@/shared/utils/modelPopulateFields';
import { upsertPaymentTimeline } from '@/modules/customer/payment/payment.service';
import { PaymentStageInput } from '@/modules/customer/payment/payment.interface';
import { PaymentStatus } from '@/modules/customer/payment/payment.helper';
import { TemplateName } from '@/shared/constants';
import {
  Status,
  UserType,
} from '@/shared/constants/enum.constant';
import { Company } from '@/modules/company/company.model';
import config from '@/shared/config/config';
import { sendEmailWithActiveTemplate } from '../communication/email/email.helper';
import { getObjectId } from '@/shared/utils/commonHelper';
import { Lead } from '../lead/lead.model';
import { Contact } from '@/modules/contacts/contacts.model';
import { UnitBookingOrHold } from '@/modules/activity/unitBookingOrHold/unitBookingOrHold.model';
import IndividualProperties from '@/modules/individualProperties/individualProperties.model';
import { Documents } from '../documents/documents.model';
import { SCOPE, STATUS } from '../documents/documents.constant';
import { CUSTOMER_DOCUMENT_STATUS } from './customer.constant';
import { normalizePaperSize } from './customer.helper';
import { LeadStage } from '@/modules/master/leadStage/leadStage.model';
import { getCustomFieldsByFormName } from '@/modules/customFields/customFields.service';

const { CustomerResponseCodes } = responseCodes;

// Helper function to get ordinal suffix for day
const getOrdinalSuffix = (day: number): string => {
  if (day > 3 && day < 21) return 'th';
  switch (day % 10) {
    case 1: return 'st';
    case 2: return 'nd';
    case 3: return 'rd';
    default: return 'th';
  }
};

// Helper function to format date with ordinal day
const formatDateWithOrdinal = (date: Date): string => {
  const day = date.getDate();
  const month = date.toLocaleDateString('en-IN', { month: 'long' });
  const year = date.getFullYear();
  return `${day}${getOrdinalSuffix(day)} ${month} ${year}`;
};

// Helper function to convert area to square meters
const convertToSquareMeters = (value: number, fromUnit: string): number => {
  const conversionFactors: Record<string, number> = {
    sqm: 1,
    sqft: 0.092903,
    sqyard: 0.836127,
    bigha: 2529.29,
    acre: 4046.86,
    hectare: 10000,
  };
  
  const factor = conversionFactors[fromUnit?.toLowerCase()] || conversionFactors.sqyard;
  return parseFloat((value * factor).toFixed(2));
}

type BrokerPaymentPayload = Pick<
  NewCreatedCustomer,
  'totalAmount' | 'bookingAmount' | 'bankAccountId'
> & {
  rentAmount?: number;
  rentAmountPaid?: number;
  depositAmount?: number;
  depositAmountPaid?: number;
};

/** Payment schedule for broker / individual-property bookings (no builder payment plan). */
const buildBrokerPropertyPaymentTimeline = (
  data: BrokerPaymentPayload,
): {
  timeline: PaymentStageInput[];
  totalAmount: number;
  paidAmount: number;
} => {
  const total = Number(data?.totalAmount ?? 0);
  const paid = Number(data?.bookingAmount ?? 0);
  const ra = Number(data?.rentAmount);
  const rp = Number(data?.rentAmountPaid);
  const da = Number(data?.depositAmount);
  const dp = Number(data?.depositAmountPaid);

  const hasRentBreakdown =
    [ra, rp, da, dp].every((n) => Number.isFinite(n)) && (ra > 0 || da > 0);

  const bookedAt = new Date();
  const bankAccountId = data?.bankAccountId;

  if (hasRentBreakdown) {
    const rentDue = Math.max(0, ra);
    const depDue = Math.max(0, da);
    const rentPaid = Math.min(Math.max(0, rp), rentDue);
    const depPaid = Math.min(Math.max(0, dp), depDue);
    return {
      timeline: [
        {
          label: 'Rent',
          dueDate: bookedAt,
          dueAmount: rentDue,
          paid: rentPaid,
          ...(bankAccountId ? { bankAccountId } : {}),
        },
        {
          label: 'Security deposit',
          dueDate: bookedAt,
          dueAmount: depDue,
          paid: depPaid,
          ...(bankAccountId ? { bankAccountId } : {}),
        },
      ],
      totalAmount: total,
      paidAmount: paid,
    };
  }

  return {
    timeline: [
      {
        label: 'Initial payment',
        dueDate: bookedAt,
        dueAmount: total,
        paid: Math.min(Math.max(0, paid), total),
        ...(bankAccountId ? { bankAccountId } : {}),
      },
    ],
    totalAmount: total,
    paidAmount: paid,
  };
};

/** Read a value from a Mongoose Map or plain object (e.g. lean() populated docs). */
const readCustomFieldFromMap = (
  mapLike: unknown,
  key: string,
): unknown => {
  if (mapLike == null || !key) return undefined;
  if (mapLike instanceof Map) {
    const v = mapLike.get(key);
    return v === undefined ? undefined : v;
  }
  const m = mapLike as Record<string, unknown> & {
    get?: (k: string) => unknown;
  };
  if (typeof m.get === 'function') {
    const viaGet = m.get(key);
    if (viaGet !== undefined && viaGet !== null) return viaGet;
  }
  if (Object.prototype.hasOwnProperty.call(m, key)) {
    return m[key as keyof typeof m];
  }
  return undefined;
};

/** Stringify custom field values for document placeholders (dropdown objects, booleans, etc.). */
const formatCustomFieldValueForDoc = (raw: unknown): string => {
  if (raw === undefined || raw === null) return '';
  if (typeof raw === 'object' && !Array.isArray(raw) && raw !== null) {
    const o = raw as Record<string, unknown>;
    if (typeof o.label === 'string' && o.label.trim() !== '') return o.label;
    if (o.value !== undefined && o.value !== null) return String(o.value);
  }
  if (typeof raw === 'boolean') return raw ? 'Yes' : 'No';
  if (Array.isArray(raw))
    return raw
      .map((x) => formatCustomFieldValueForDoc(x))
      .filter((s) => s !== '')
      .join(', ');
  return String(raw);
};

/** Document placeholder: convert number custom fields marked isUOM from company default UOM to sq.m */
const formatCustomFieldForDocument = (
  fieldDef: { type: string; isUOM?: boolean },
  raw: unknown,
  defaultUom: string,
): string => {
  if (fieldDef.type === 'number' && fieldDef.isUOM) {
    if (raw !== undefined && raw !== null) {
      let n: number | undefined;
      if (typeof raw === 'number' && Number.isFinite(raw)) n = raw;
      else if (typeof raw === 'string' && raw.trim() !== '') {
        const parsed = Number(raw);
        if (Number.isFinite(parsed)) n = parsed;
      }
      if (n !== undefined) return String(convertToSquareMeters(n, defaultUom));
    }
  }
  return formatCustomFieldValueForDoc(raw);
};

/** Indian-style grouping for display (e.g. 75,00,000) */
const formatIndianNumber = (n: number | undefined | null): string => {
  if (n === undefined || n === null || Number.isNaN(Number(n))) return '';
  return Number(n).toLocaleString('en-IN');
};

const convertLessThanThousand = (n: number): string => {
  const ones = [
    '',
    'One',
    'Two',
    'Three',
    'Four',
    'Five',
    'Six',
    'Seven',
    'Eight',
    'Nine',
  ];
  const teens = [
    'Ten',
    'Eleven',
    'Twelve',
    'Thirteen',
    'Fourteen',
    'Fifteen',
    'Sixteen',
    'Seventeen',
    'Eighteen',
    'Nineteen',
  ];
  const tens = [
    '',
    '',
    'Twenty',
    'Thirty',
    'Forty',
    'Fifty',
    'Sixty',
    'Seventy',
    'Eighty',
    'Ninety',
  ];
  if (n === 0) return '';
  if (n < 10) return ones[n];
  if (n < 20) return teens[n - 10];
  if (n < 100)
    return (
      tens[Math.floor(n / 10)] + (n % 10 ? ` ${ones[n % 10]}` : '')
    ).trim();
  return (
    `${ones[Math.floor(n / 100)]} Hundred` +
    (n % 100 ? ` ${convertLessThanThousand(n % 100)}` : '')
  ).trim();
};

/** Whole rupee amounts in words (Indian English), for agreements / advances */
const numberToWords = (num: number): string => {
  if (num < 0 || Number.isNaN(num)) return '';
  const n = Math.round(num);
  if (n === 0) return 'Zero';

  if (n >= 10000000) {
    const crore = Math.floor(n / 10000000);
    const remainder = n % 10000000;
    return (
      `${convertLessThanThousand(crore)} Crore` +
      (remainder ? ` ${numberToWords(remainder)}` : '')
    ).trim();
  }
  if (n >= 100000) {
    const lakh = Math.floor(n / 100000);
    const remainder = n % 100000;
    return (
      `${convertLessThanThousand(lakh)} Lakh` +
      (remainder ? ` ${numberToWords(remainder)}` : '')
    ).trim();
  }
  if (n >= 1000) {
    const thousand = Math.floor(n / 1000);
    const remainder = n % 1000;
    return (
      `${convertLessThanThousand(thousand)} Thousand` +
      (remainder ? ` ${convertLessThanThousand(remainder)}` : '')
    ).trim();
  }
  return convertLessThanThousand(n);
}

export function applyCustomerListingCreatedByScope(
  matchFilter: Record<string, unknown>,
  allowedIds: Types.ObjectId[],
) {
  const allowed = new Set(allowedIds.map((id) => id.toString()));
  const toObjectId = (v: unknown): Types.ObjectId =>
    v instanceof Types.ObjectId ? v : getObjectId(String(v));

  if (matchFilter.createdBy !== undefined && matchFilter.createdBy !== null) {
    const existing = matchFilter.createdBy;
    if (
      typeof existing === 'object' &&
      existing !== null &&
      '$in' in existing &&
      Array.isArray((existing as { $in: unknown[] }).$in)
    ) {
      const intersected = (existing as { $in: unknown[] }).$in
        .map(toObjectId)
        .filter((id) => allowed.has(id.toString()));
      matchFilter.createdBy =
        intersected.length > 0 ? { $in: intersected } : { $in: [] };
    } else {
      const cid = toObjectId(existing);
      matchFilter.createdBy = allowed.has(cid.toString())
        ? cid
        : { $in: [] };
    }
  } else {
    matchFilter.createdBy = { $in: allowedIds };
  }
}

export const createCustomer = async (
  data: NewCreatedCustomer,
  userId: mongoose.Types.ObjectId,
  companyId: mongoose.Types.ObjectId,
): Promise<boolean | null> => {
  try {
    const newCustomer = await Customer.create(data);

    // If this customer is created from a broker flow (unitBookingOrHold -> property),
    // copy property title into customer for easier UI consumption.
    let bookingLinkedPropertyId: string | undefined;
    if (data?.unitBookingOrHold) {
      const uboh = await UnitBookingOrHold.findById(
        getObjectId(data.unitBookingOrHold as any),
      )
        .select('property')
        .lean();

      const propertyId = (uboh as any)?.property;
      if (propertyId) {
        bookingLinkedPropertyId = String(propertyId);
        const prop = await IndividualProperties.findById(
          getObjectId(propertyId),
        )
          .select('title')
          .lean();

        const propertyTitle = (prop as any)?.title;
        if (propertyTitle)
          await Customer.findByIdAndUpdate(newCustomer._id, {
            $set: { propertyTitle },
          });
      }
    }

    const customer = await Customer.findById(newCustomer._id)
      .populate([
        { path: 'createdBy', select: 'email' },
        { path: 'leadId', select: 'contactDetails' },
        {
          path: 'contactId',
          select: 'phone addressLine1 addressLine',
        },
      ])
      .lean<PopulatedCustomer>();

    if (newCustomer?.leadId)
      await Lead.findByIdAndUpdate(newCustomer?.leadId, {
        $set: {
          isConvertedToCustomer: true,
        },
      });

    const salesForTypeCheck = (customer?.sales ?? data?.sales ?? []) as any[];
    const isBrokerCustomer =
      salesForTypeCheck.some(
        (s) => String(s?.kind) === 'Property' && s?.property,
      ) || Boolean(bookingLinkedPropertyId);

    // Generate remaining payment timeline only for builder customers (not broker)
    if (!isBrokerCustomer)
      try {
        // Get customer with payment plan to generate timeline
        const customerWithPlan = await Customer.findById(newCustomer._id)
          .populate('unitBookingOrHold')
          .populate('paymentPlan')
          .lean();

        const unitBooking = (customerWithPlan as any)?.unitBookingOrHold;
        const paymentPlan = (customerWithPlan as any)?.paymentPlan;

        if (unitBooking && paymentPlan) {
          // Get unit details for total amount
          const unitDetails = await UnitBookingOrHold.findById(unitBooking._id)
            .populate('unit')
            .lean();

          if (unitDetails && (unitDetails as any).unit) {
            // Generate timeline stages
            const timeline: any[] = [];
            const currentDate = new Date();

            // First, add the existing booking amount stage
            timeline.push({
              label: 'Booking Amount',
              paid: data?.bookingAmount,
              status: PaymentStatus.PAID,
              dueAmount: data?.bookingAmount || 0, // Remaining amount to be paid
              bankAccountId: data?.bankAccountId,
            });

            // Generate installment stages only from remaining amount after booking
            if (
              paymentPlan.installmentMonth &&
              paymentPlan.installmentMonth > 0
            ) {
              const remainingAfterBooking =
                data?.totalAmount - (data?.bookingAmount || 0);

              const installmentAmount =
                remainingAfterBooking / paymentPlan.installmentMonth;

              for (let i = 1; i <= paymentPlan.installmentMonth; i++) {
                const stageLabel = `Installment ${i}`;
                const dueDate = new Date(
                  currentDate.getFullYear(),
                  currentDate.getMonth() + i,
                  1,
                );

                // For the last installment, adjust to ensure total doesn't exceed totalAmount
                let finalInstallmentAmount = installmentAmount;
                if (i === paymentPlan.installmentMonth) {
                  // Last installment: remaining amount = totalAmount - (bookingAmount + (installmentAmount * (i-1)))
                  const totalPaidSoFar =
                    (data?.bookingAmount || 0) + installmentAmount * (i - 1);
                  finalInstallmentAmount = data?.totalAmount - totalPaidSoFar;
                }

                timeline.push({
                  label: stageLabel,
                  dueDate,
                  dueAmount: finalInstallmentAmount,
                  paid: 0,
                  status: PaymentStatus.PENDING,
                });
              }
            }

            if (timeline.length > 0)
              await upsertPaymentTimeline({
                customerId: newCustomer._id.toString(),
                company: companyId,
                totalAmount: data?.totalAmount || 0,
                paidAmount: data?.bookingAmount || 0,
                timeline,
                createdBy: userId,
              });
          }
        }
      } catch (paymentError) {
        // Fallback to basic timeline if payment plan generation fails
        console.warn(
          'Payment plan generation failed, using basic timeline:',
          paymentError,
        );

        await upsertPaymentTimeline({
          customerId: newCustomer._id.toString(),
          company: companyId,
          totalAmount: data?.totalAmount,
          paidAmount: data?.bookingAmount,
          timeline: [
            {
              label: 'Booking Amount',
              paid: data?.bookingAmount,
              status: PaymentStatus.PAID,
              dueAmount: data?.totalAmount - data?.bookingAmount,
              bankAccountId: data?.bankAccountId,
            },
          ],
          createdBy: userId,
        });
      }

    if (isBrokerCustomer) {
      try {
        const { timeline, totalAmount, paidAmount } =
          buildBrokerPropertyPaymentTimeline(data);
        await upsertPaymentTimeline({
          customerId: newCustomer._id.toString(),
          company: companyId,
          totalAmount,
          paidAmount,
          timeline,
          createdBy: userId,
        });
      } catch (brokerPayErr) {
        console.warn(
          'Broker property payment timeline failed:',
          brokerPayErr,
        );
      }
    }

    const first = customer?.contactId?.firstName?.trim() || '';
    const last = customer?.contactId?.lastName?.trim() || '';

    const nameSlug = [first, last]
      .filter(Boolean)
      .join('-')
      .replace(/\s+/g, '-')
      .toLowerCase();

    const slug = nameSlug || 'lead';
    const leadUrl = `${config.clientUrl}/leads/${encodeURIComponent(slug)}?id=${data?.leadId}`;

    const sales = customer?.sales ?? data?.sales ?? [];
    const hasProject = sales.some((s) => s?.kind === 'Project' && s?.project);
    const hasProperty = sales.some(
      (s) => s?.kind === 'Property' && s?.property,
    );
    const source = hasProject
      ? 'Project'
      : hasProperty
        ? 'Property'
        : undefined;

    await sendEmailWithActiveTemplate({
      to: customer?.createdBy?.email as string,
      companyId: companyId,
      scenario: TemplateName.LeadToCustomer,
      templateParams: {
        fullName: `${newCustomer.name}`,
        leadName: customer?.leadId?.contactDetails?.name,
        contact:
          customer?.contactId?.phone[0].countryCode +
          '' +
          customer?.contactId?.phone[0].number,
        leadUrl,
        address: customer?.contactId?.addressLine1,
        source,
      },
    });

    return true;
  } catch (error) {
    if (error instanceof ApiError) throw error;

    throw new ApiError(
      defaultStatus.OK,
      'Failed to create customer',
      true,
      '',
      CustomerResponseCodes.COMPANY_ERROR,
    );
  }
};

export const getCustomerById = async (
  id: mongoose.Types.ObjectId,
  fields?: string,
  populate?: string,
) => {
  const customer = await getEntityByIdWithQueryString({
    model: Customer,
    entityId: id,
    fields,
    populate,
    responseCode: CustomerResponseCodes.COMPANY_NOT_FOUND,
  });

  // Enrich unitBookingOrHold to return readable fields even if populate is not provided.
  // This endpoint is used by the customer detail screen, so we embed:
  // - Builder: project.projectName, unit.unitNumber, salesAt
  // - Broker: property.title (fallback to customer.propertyTitle), salesAt
  const ubohId =
    (customer as any)?.unitBookingOrHold?._id ||
    (customer as any)?.unitBookingOrHold;
  if (ubohId) {
    const uboh = await UnitBookingOrHold.findById(getObjectId(ubohId as any))
      .populate([
        { 
          path: 'project', 
          select: 'projectName',
          populate: [
            { path: 'state', select: 'name' },
            { path: 'city', select: 'name' }
          ]
        },
        {
          path: 'unit',
          select: 'unitNumber block floor price size customFields propertyType',
          populate: [{ path: 'propertyType', select: 'name' }],
        },
        {
          path: 'property',
          select: 'title bedrooms project configuration locality city state',
          populate: [
            { path: 'project', select: 'projectName' },
            { path: 'configuration', select: 'name' },
            { path: 'locality', select: 'name' },
            { path: 'city', select: 'name' },
            { path: 'state', select: 'name' },
          ],
        },
        { path: 'soldBy', select: 'firstName lastName phone' },
      ])
      .lean();

    if (uboh) {
      const project = (uboh as any)?.project;
      const unit = (uboh as any)?.unit;
      const property = (uboh as any)?.property;

      const contactIdRef = (customer as any)?.contactId;
      const contactObjectId =
        contactIdRef &&
        typeof contactIdRef === 'object' &&
        (contactIdRef as any)._id
          ? (contactIdRef as any)._id
          : contactIdRef;
      const contactForDocs = contactObjectId
        ? await Contact.findById(getObjectId(contactObjectId as any))
            .select('addressLine1 addressLine2 pinCode')
            .populate([
              { path: 'state', select: 'name' },
              { path: 'city', select: 'name' },
              { path: 'country', select: 'name' },
            ])
            .lean()
        : null;

      const companyId = getObjectId(customer.company);
      const companyForDocs = companyId
        ? await Company.findById(companyId)
            .select('address pincode')
            .populate([
              { path: 'area', select: 'name' },
              { path: 'city', select: 'name' },
              { path: 'state', select: 'name' },
              { path: 'country', select: 'name' },
            ])
            .lean()
        : null;

      const promoterAdminUser = companyId
        ? await User.findOne({
            'company.id': companyId,
            userType: UserType.ADMIN,
            status: Status.ACTIVE,
            isDeleted: { $ne: true },
          })
            .sort({ createdAt: 1 })
            .select('firstName lastName')
            .lean()
        : null;

      const promoterNameResolved = promoterAdminUser
        ? [promoterAdminUser.firstName, promoterAdminUser.lastName]
            .filter((x) => x !== undefined && x !== null && String(x).trim() !== '')
            .join(' ')
            .trim()
        : '';

      const [unitCustomFieldDefs, bookingCustomFieldDefs] = await Promise.all([
        getCustomFieldsByFormName(getObjectId(customer.company), 'unit'),
        getCustomFieldsByFormName(getObjectId(customer.company), 'book_or_hold'),
      ]);

      let relatedDocuments: any[] = [];

      if (project?._id)
        relatedDocuments = await Documents.find({
          $or: [
            { projectIds: { $in: [getObjectId(project._id)] } },
            { scope: SCOPE.ALL_PROJECTS },
          ],
          status: STATUS.PUBLISHED,
          companyId: getObjectId(customer.company),
        }).lean();

      const customerDocumentMetaMap = new Map<
        string,
        {
          status: string;
          generatedAt?: Date;
          sentAt?: Date;
          content?: string;
          url?: string;
        }
      >();

      (customer as any)?.documents?.forEach((d: any) => {
        const documentId = d?.document?._id || d?.document;

        if (!documentId) return;

        customerDocumentMetaMap.set(d?.document?.toString(), {
          status: d.status,
          generatedAt: d.generatedAt,
          sentAt: d.sentAt,
          url: d.url,
        });
      });

      const documentsWithStatus = relatedDocuments.map((doc: any) => {
        const docId = doc?._id?.toString();

        const meta = docId ? customerDocumentMetaMap.get(docId) : undefined;

        return {
          ...doc,

          // 🔑 SINGLE SOURCE OF TRUTH
          status: meta?.status ?? CUSTOMER_DOCUMENT_STATUS.NOT_GENERATED,
          generatedAt: meta?.generatedAt ?? null,
          sentAt: meta?.sentAt ?? null,
          url: meta?.url ?? null,

          content: doc?.content ?? null,
          paperSize: normalizePaperSize(doc.paperSize),
        };
      });

      // Keep full UnitBookingOrHold document (persons/docs/customFields/etc.)
      // and add a shaped view for the UI.
      (customer as any).unitBookingOrHold = {
        ...(uboh as any),
        id: (uboh as any)?._id,
        salesAt: (uboh as any)?.createdAt,
        project: project
          ? {
              id: project?._id,
              projectName: project?.projectName,
            }
          : null,
        unit: unit
          ? {
              id: unit?._id,
              unitNumber: unit?.unitNumber,
              block: unit?.block,
              floor: unit?.floor,
            }
          : null,
        property: property
          ? {
              id: property?._id,
              title: property?.title,
              bedrooms: (property as any)?.bedrooms,
              projectName: (property as any)?.project?.projectName,
              configurationName: (property as any)?.configuration?.name,
              localityName: (property as any)?.locality?.name,
              cityName: (property as any)?.city?.name,
              stateName: (property as any)?.state?.name,
            }
          : (customer as any)?.propertyTitle
            ? {
                id: (uboh as any)?.property,
                title: (customer as any)?.propertyTitle,
              }
            : null,

        documents: documentsWithStatus,
        /*eslint-disable camelcase */
        content: (() => {
          const persons = (uboh as any)?.persons as { name: string }[] | undefined;
          const namesJoined =
            persons && persons.length > 0
              ? persons.map((p) => p.name).join(', ')
              : customer?.name;
          const primaryName =
            persons && persons.length > 0 ? persons[0].name : customer?.name;
          const bookingAt = uboh?.createdAt
            ? new Date(uboh.createdAt as any)
            : null;
          const unitPrice = unit?.price;
          const bookingAmt = uboh?.bookingAmount;
          const defaultUom = (customer?.company as any)?.defaultUOM || 'sqyard';
          const reraSqM =
            unit?.size !== undefined && unit?.size !== null
              ? convertToSquareMeters(unit.size, defaultUom)
              : '';

          return {
            customer_name: namesJoined,
            customer_names: namesJoined,
            primary_customer_name: primaryName || '',
            allottee_count: persons?.length ? String(persons.length) : '1',
            customer_email: (customer?.contactId as any)?.email || '',
            customer_address: (() => {
              const c = contactForDocs || (customer?.contactId as any);
              if (!c) return '';
              const pin =
                c.pinCode !== undefined && c.pinCode !== null
                  ? String(c.pinCode)
                  : '';
              return [
                c.addressLine1,
                c.addressLine2,
                c.city?.name,
                c.state?.name,
                c.country?.name,
                pin,
              ]
                .filter(
                  (x) =>
                    x !== undefined &&
                    x !== null &&
                    String(x).trim() !== '',
                )
                .join(', ');
            })(),
            project_name: project?.projectName,
            project_state: (project as any)?.state?.name,
            project_city: (project as any)?.city?.name,
            property_name: property?.title || '',
            property_id: property?._id ? String(property._id) : '',
            unit_type: (unit as any)?.propertyType?.name || '',
            unit_number: unit?.unitNumber,
            unit_carpet_area: reraSqM,
            rera_carpet_sq_m: reraSqM,
            floor: unit?.floor ?? '',
            floor_number:
              unit?.floor !== undefined &&
              unit?.floor !== null &&
              !Number.isNaN(Number(unit.floor))
                ? `${unit.floor}${getOrdinalSuffix(Number(unit.floor))}`
                : '',
            block: unit?.block,
            tower: unit?.block,
            wing_name: unit?.block || '',
            basic_price: unitPrice,
            agreement_value: formatIndianNumber(unitPrice),
            booking_amount: bookingAmt,
            advance_amount: formatIndianNumber(bookingAmt),
            advance_amount_words:
              bookingAmt !== undefined &&
              bookingAmt !== null &&
              !Number.isNaN(Number(bookingAmt))
                ? `${numberToWords(Number(bookingAmt))} Only`
                : '',
            balance_amount:
              unitPrice !== undefined &&
              unitPrice !== null &&
              bookingAmt !== undefined &&
              bookingAmt !== null &&
              !Number.isNaN(Number(unitPrice)) &&
              !Number.isNaN(Number(bookingAmt))
                ? formatIndianNumber(
                    Math.max(0, Number(unitPrice) - Number(bookingAmt)),
                  )
                : '',
            booking_date: bookingAt ? formatDateWithOrdinal(bookingAt) : '',
            booking_month: bookingAt
              ? bookingAt.toLocaleDateString('en-IN', { month: 'long' })
              : '',
            booking_year: bookingAt
              ? String(bookingAt.getFullYear())
              : '',
            agreement_date: bookingAt
              ? `${bookingAt.getDate()}${getOrdinalSuffix(bookingAt.getDate())}`
              : '',
            agreement_month_words: bookingAt
              ? bookingAt.toLocaleDateString('en-IN', { month: 'long' })
              : '',
            agreement_year_words: bookingAt
              ? numberToWords(bookingAt.getFullYear())
              : '',
            company_name: (customer?.company as any)?.name,
            promoter_name: promoterNameResolved,
            promoter_address: (() => {
              const c = companyForDocs as any;
              if (!c) return '';
              const pin =
                c.pincode !== undefined && c.pincode !== null
                  ? String(c.pincode)
                  : '';
              return [
                c.address,
                c.area?.name,
                c.city?.name,
                c.state?.name,
                c.country?.name,
                pin,
              ]
                .filter(
                  (x) =>
                    x !== undefined &&
                    x !== null &&
                    String(x).trim() !== '',
                )
                .join(', ');
            })(),
            sales_manager_name: (uboh?.soldBy as any)?.firstName,
            current_date: new Date().toLocaleDateString('en-IN'),

            ...((unitCustomFieldDefs as any[])?.reduce((acc, fieldDef) => {
              const fieldKey = `unit_custom_${fieldDef.key}`;
              const raw =
                readCustomFieldFromMap(
                  (unit as any)?.customFields,
                  fieldDef.key,
                ) ??
                readCustomFieldFromMap(
                  (uboh as any)?.customFields,
                  fieldDef.key,
                );
              acc[fieldKey] = formatCustomFieldForDocument(
                fieldDef,
                raw,
                defaultUom,
              );
              return acc;
            }, {} as Record<string, any>) || {}),
            ...((bookingCustomFieldDefs as any[])?.reduce((acc, fieldDef) => {
              const fieldKey = `booking_custom_${fieldDef.key}`;
              const raw = readCustomFieldFromMap(
                (uboh as any)?.customFields,
                fieldDef.key,
              );
              acc[fieldKey] = formatCustomFieldForDocument(
                fieldDef,
                raw,
                defaultUom,
              );
              return acc;
            }, {} as Record<string, any>) || {}),
          };
        })(),
      };
    }
  }

  return customer;
};

export const updateCustomer = async (
  id: string,
  updateData: UpdateCustomerBody,
): Promise<boolean | null> => {
  let customer: boolean | null;

  try {
    customer = await Customer.findByIdAndUpdate(id, updateData, { new: true });
  } catch (_error) {
    throw new ApiError(
      defaultStatus.OK,
      'Failed to update customer',
      true,
      '',
      CustomerResponseCodes.COMPANY_ERROR,
    );
  }

  if (!customer)
    throw new ApiError(
      defaultStatus.OK,
      'Customer not found',
      true,
      '',
      CustomerResponseCodes.COMPANY_NOT_FOUND,
    );

  return true;
};

export const deleteCustomer = async (id: string): Promise<boolean | null> => {
  try {
    let customer: boolean | null;
    customer = await Customer.findByIdAndDelete(id);

    if (!customer)
      throw new ApiError(
        defaultStatus.OK,
        'Customer not found',
        true,
        '',
        CustomerResponseCodes.COMPANY_NOT_FOUND,
      );

    await User.deleteMany({ 'customer.id': id });

    return true;
  } catch (_error) {
    throw new ApiError(
      defaultStatus.OK,
      'Failed to delete customer',
      true,
      '',
      CustomerResponseCodes.COMPANY_ERROR,
    );
  }
};

export const queryCustomers = async (filter, options = {}) => {
  try {
    const matchFilter: Record<string, unknown> = { ...filter };

    const project = matchFilter.project;
    const salesBy = matchFilter.salesBy;
    const salesDate = matchFilter.salesDate;

    let salesAtRange: { $gte: Date; $lte: Date } | undefined;

    delete matchFilter.project;
    delete matchFilter.salesBy;
    delete matchFilter.salesDate;

    if (matchFilter.excludeCancelled === 'true' || matchFilter.excludeCancelled === true) {
      matchFilter.bookingStatus = { $ne: 'cancelled' };
    }
    
    delete matchFilter.excludeCancelled;

    // Convert company to ObjectId if string
    if (matchFilter.company && typeof matchFilter.company === 'string')
      matchFilter.company = getObjectId(matchFilter.company);

    if (salesDate && typeof salesDate === 'string') {
      const salesDateStr = salesDate.trim();
      const dateParts = salesDateStr.split(',').map((p) => p.trim());

      // Frontend always sends a range: YYYY-MM-DD,YYYY-MM-DD
      if (dateParts.length === 2 && dateParts[0] && dateParts[1]) {
        const start = new Date(dateParts[0]);
        const end = new Date(dateParts[1]);

        if (!Number.isNaN(start.getTime()) && !Number.isNaN(end.getTime())) {
          start.setHours(0, 0, 0, 0);
          end.setHours(23, 59, 59, 999);
          salesAtRange = { $gte: start, $lte: end };
        }
      }
    }

    if (matchFilter.leadId && typeof matchFilter.leadId === 'string')
      matchFilter.leadId = getObjectId(matchFilter.leadId);

    const aggregation: PipelineStage[] = [
      { $match: matchFilter },

      {
        $lookup: {
          from: 'companies',
          localField: 'company',
          foreignField: '_id',
          as: 'company',
        },
      },

      // Payments lookup
      {
        $lookup: {
          from: 'customerpayments',
          localField: '_id',
          foreignField: 'customerId',
          as: 'payments',
        },
      },

      // Sales.soldBy lookup
      {
        $lookup: {
          from: 'users',
          localField: 'sales.soldBy',
          foreignField: '_id',
          as: 'salesSoldBy',
        },
      },

      // Sales.project lookup
      {
        $lookup: {
          from: 'projects',
          localField: 'sales.project',
          foreignField: '_id',
          as: 'salesProject',
        },
      },

      // Sales.property lookup
      {
        $lookup: {
          from: 'individualproperties',
          localField: 'sales.property',
          foreignField: '_id',
          as: 'salesProperty',
        },
      },

      // Enrich broker sales properties: project, configuration, locality, city, state
      {
        $lookup: {
          from: 'projects',
          localField: 'salesProperty.project',
          foreignField: '_id',
          as: 'salesPropertyProjects',
        },
      },
      {
        $lookup: {
          from: 'configurations',
          localField: 'salesProperty.configuration',
          foreignField: '_id',
          as: 'salesPropertyConfigurations',
        },
      },
      {
        $lookup: {
          from: 'areas',
          localField: 'salesProperty.locality',
          foreignField: '_id',
          as: 'salesPropertyAreas',
        },
      },
      {
        $lookup: {
          from: 'cities',
          localField: 'salesProperty.city',
          foreignField: '_id',
          as: 'salesPropertyCities',
        },
      },
      {
        $lookup: {
          from: 'states',
          localField: 'salesProperty.state',
          foreignField: '_id',
          as: 'salesPropertyStates',
        },
      },

      // UnitBookingOrHold lookup (Customer.unitBookingOrHold -> UnitBookingOrHold)
      {
        $lookup: {
          from: 'unitbookingorholds',
          localField: 'unitBookingOrHold',
          foreignField: '_id',
          as: 'unitBookingOrHoldDoc',
        },
      },

      // Apply UI filters against unit booking/hold fields (authoritative for list)
      // while keeping compatibility with older sales.* filtering.
      ...(() => {
        const parseObjectIds = (value?: string) => {
          if (!value) return undefined;

          const ids = value.split(',').map((id) => getObjectId(id.trim()));

          return ids.length === 1 ? ids[0] : { $in: ids };
        };
        
        const postMatch: Record<string, unknown> = {};

        if (project && typeof project === 'string') {
          const projectFilter = parseObjectIds(project);

          postMatch.$or = [
            { 'unitBookingOrHoldDoc.project': projectFilter },
            { 'sales.project': projectFilter },
          ];
        }

        if (salesBy && typeof salesBy === 'string') {
          const salesByFilter = parseObjectIds(salesBy);

          const cond = {
            $or: [
              { 'unitBookingOrHoldDoc.soldBy': salesByFilter },
              { 'sales.soldBy': salesByFilter },
            ],
          };

          if (postMatch.$or) {
            // Combine existing $or (project) with soldBy via $and
            postMatch.$and = [{ $or: postMatch.$or }, cond];
            delete postMatch.$or;
          } else {
            Object.assign(postMatch, cond);
          }
        }

        if (salesAtRange) {
          const cond = {
            $or: [
              { 'unitBookingOrHoldDoc.createdAt': salesAtRange },
              { 'sales.salesAt': salesAtRange },
            ],
          };

          if (postMatch.$and) {
            (postMatch.$and as unknown[]).push(cond);
          } else if (postMatch.$or) {
            postMatch.$and = [{ $or: postMatch.$or }, cond];
            delete postMatch.$or;
          } else {
            Object.assign(postMatch, cond);
          }
        }

        return Object.keys(postMatch).length > 0 ? [{ $match: postMatch }] : [];
      })(),

      // Unit lookup (UnitBookingOrHold.unit -> Unit)
      {
        $lookup: {
          from: 'units',
          localField: 'unitBookingOrHoldDoc.unit',
          foreignField: '_id',
          as: 'unitDoc',
        },
      },

      // Project lookup for unit booking/hold (UnitBookingOrHold.project -> Project)
      {
        $lookup: {
          from: 'projects',
          localField: 'unitBookingOrHoldDoc.project',
          foreignField: '_id',
          as: 'unitProjectDoc',
        },
      },

      // SoldBy lookup for unit booking/hold (UnitBookingOrHold.soldBy -> User)
      {
        $lookup: {
          from: 'users',
          localField: 'unitBookingOrHoldDoc.soldBy',
          foreignField: '_id',
          as: 'unitSoldBy',
        },
      },

      // Property lookup for broker flow (UnitBookingOrHold.property -> individualProperties)
      {
        $lookup: {
          from: 'individualproperties',
          localField: 'unitBookingOrHoldDoc.property',
          foreignField: '_id',
          as: 'unitPropertyDoc',
        },
      },
      {
        $lookup: {
          from: 'projects',
          let: {
            pid: { $arrayElemAt: ['$unitPropertyDoc.project', 0] },
          },
          pipeline: [
            { $match: { $expr: { $eq: ['$_id', '$$pid'] } } },
            { $project: { projectName: 1 } },
          ],
          as: 'ubohPropertyProjectDoc',
        },
      },
      {
        $lookup: {
          from: 'configurations',
          let: {
            cid: { $arrayElemAt: ['$unitPropertyDoc.configuration', 0] },
          },
          pipeline: [
            { $match: { $expr: { $eq: ['$_id', '$$cid'] } } },
            { $project: { name: 1 } },
          ],
          as: 'ubohPropertyConfigurationDoc',
        },
      },
      {
        $lookup: {
          from: 'areas',
          let: {
            aid: { $arrayElemAt: ['$unitPropertyDoc.locality', 0] },
          },
          pipeline: [
            { $match: { $expr: { $eq: ['$_id', '$$aid'] } } },
            { $project: { name: 1 } },
          ],
          as: 'ubohPropertyLocalityDoc',
        },
      },
      {
        $lookup: {
          from: 'cities',
          let: {
            cid: { $arrayElemAt: ['$unitPropertyDoc.city', 0] },
          },
          pipeline: [
            { $match: { $expr: { $eq: ['$_id', '$$cid'] } } },
            { $project: { name: 1 } },
          ],
          as: 'ubohPropertyCityDoc',
        },
      },
      {
        $lookup: {
          from: 'states',
          let: {
            sid: { $arrayElemAt: ['$unitPropertyDoc.state', 0] },
          },
          pipeline: [
            { $match: { $expr: { $eq: ['$_id', '$$sid'] } } },
            { $project: { name: 1 } },
          ],
          as: 'ubohPropertyStateDoc',
        },
      },

      // Add totals
      {
        $addFields: {
          totalAmount: { $sum: '$payments.totalAmount' },
          paidAmount: { $sum: '$payments.paidAmount' },

          companyName: { $arrayElemAt: ['$company.name', 0] },

          // Map sales with populated data
          sales: {
            $map: {
              input: '$sales',
              as: 'sale',
              in: {
                kind: '$$sale.kind',
                salesAt: '$$sale.salesAt',
                notes: '$$sale.notes',

                soldBy: {
                  $arrayElemAt: [
                    {
                      $map: {
                        input: {
                          $filter: {
                            input: '$salesSoldBy',
                            cond: { $eq: ['$$this._id', '$$sale.soldBy'] },
                          },
                        },
                        as: 'u',
                        in: {
                          id: '$$u._id',
                          firstName: '$$u.firstName',
                          lastName: '$$u.lastName',
                        },
                      },
                    },
                    0,
                  ],
                },

                project: {
                  $arrayElemAt: [
                    {
                      $map: {
                        input: {
                          $filter: {
                            input: '$salesProject',
                            cond: { $eq: ['$$this._id', '$$sale.project'] },
                          },
                        },
                        as: 'p',
                        in: { projectName: '$$p.projectName' },
                      },
                    },
                    0,
                  ],
                },

                property: {
                  $arrayElemAt: [
                    {
                      $map: {
                        input: {
                          $filter: {
                            input: '$salesProperty',
                            cond: { $eq: ['$$this._id', '$$sale.property'] },
                          },
                        },
                        as: 'pr',
                        in: {
                          title: '$$pr.title',
                          bedrooms: '$$pr.bedrooms',
                          projectName: {
                            $let: {
                              vars: {
                                doc: {
                                  $arrayElemAt: [
                                    {
                                      $filter: {
                                        input: '$salesPropertyProjects',
                                        as: 'sp',
                                        cond: {
                                          $eq: ['$$sp._id', '$$pr.project'],
                                        },
                                      },
                                    },
                                    0,
                                  ],
                                },
                              },
                              in: '$$doc.projectName',
                            },
                          },
                          configurationName: {
                            $let: {
                              vars: {
                                doc: {
                                  $arrayElemAt: [
                                    {
                                      $filter: {
                                        input: '$salesPropertyConfigurations',
                                        as: 'c',
                                        cond: {
                                          $eq: ['$$c._id', '$$pr.configuration'],
                                        },
                                      },
                                    },
                                    0,
                                  ],
                                },
                              },
                              in: '$$doc.name',
                            },
                          },
                          localityName: {
                            $let: {
                              vars: {
                                doc: {
                                  $arrayElemAt: [
                                    {
                                      $filter: {
                                        input: '$salesPropertyAreas',
                                        as: 'a',
                                        cond: {
                                          $eq: ['$$a._id', '$$pr.locality'],
                                        },
                                      },
                                    },
                                    0,
                                  ],
                                },
                              },
                              in: '$$doc.name',
                            },
                          },
                          cityName: {
                            $let: {
                              vars: {
                                doc: {
                                  $arrayElemAt: [
                                    {
                                      $filter: {
                                        input: '$salesPropertyCities',
                                        as: 'ct',
                                        cond: {
                                          $eq: ['$$ct._id', '$$pr.city'],
                                        },
                                      },
                                    },
                                    0,
                                  ],
                                },
                              },
                              in: '$$doc.name',
                            },
                          },
                          stateName: {
                            $let: {
                              vars: {
                                doc: {
                                  $arrayElemAt: [
                                    {
                                      $filter: {
                                        input: '$salesPropertyStates',
                                        as: 'st',
                                        cond: {
                                          $eq: ['$$st._id', '$$pr.state'],
                                        },
                                      },
                                    },
                                    0,
                                  ],
                                },
                              },
                              in: '$$doc.name',
                            },
                          },
                        },
                      },
                    },
                    0,
                  ],
                },
              },
            },
          },

          // Replace raw ObjectId with human-readable unit details
          unitBookingOrHold: {
            $let: {
              vars: {
                uboh: { $arrayElemAt: ['$unitBookingOrHoldDoc', 0] },
                unit: { $arrayElemAt: ['$unitDoc', 0] },
                proj: { $arrayElemAt: ['$unitProjectDoc', 0] },
                soldBy: { $arrayElemAt: ['$unitSoldBy', 0] },
                prop: { $arrayElemAt: ['$unitPropertyDoc', 0] },
              },
              in: {
                $cond: [
                  { $ifNull: ['$$uboh', false] },
                  {
                    id: '$$uboh._id',
                    unitNumber: '$$unit.unitNumber',
                    block: '$$unit.block',
                    floor: '$$unit.floor',
                    salesAt: '$$uboh.createdAt',
                    project: {
                      $cond: [
                        { $ifNull: ['$$proj', false] },
                        {
                          id: '$$proj._id',
                          projectName: '$$proj.projectName',
                        },
                        null,
                      ],
                    },
                    property: {
                      $cond: [
                        {
                          $or: [
                            { $ifNull: ['$propertyTitle', false] },
                            { $ifNull: ['$$prop', false] },
                          ],
                        },
                        {
                          id: { $ifNull: ['$$prop._id', '$$uboh.property'] },
                          title: {
                            $ifNull: ['$propertyTitle', '$$prop.title'],
                          },
                          bedrooms: '$$prop.bedrooms',
                          projectName: {
                            $let: {
                              vars: {
                                doc: {
                                  $arrayElemAt: ['$ubohPropertyProjectDoc', 0],
                                },
                              },
                              in: '$$doc.projectName',
                            },
                          },
                          configurationName: {
                            $let: {
                              vars: {
                                doc: {
                                  $arrayElemAt: [
                                    '$ubohPropertyConfigurationDoc',
                                    0,
                                  ],
                                },
                              },
                              in: '$$doc.name',
                            },
                          },
                          localityName: {
                            $let: {
                              vars: {
                                doc: {
                                  $arrayElemAt: ['$ubohPropertyLocalityDoc', 0],
                                },
                              },
                              in: '$$doc.name',
                            },
                          },
                          cityName: {
                            $let: {
                              vars: {
                                doc: {
                                  $arrayElemAt: ['$ubohPropertyCityDoc', 0],
                                },
                              },
                              in: '$$doc.name',
                            },
                          },
                          stateName: {
                            $let: {
                              vars: {
                                doc: {
                                  $arrayElemAt: ['$ubohPropertyStateDoc', 0],
                                },
                              },
                              in: '$$doc.name',
                            },
                          },
                        },
                        null,
                      ],
                    },
                    soldBy: {
                      $cond: [
                        { $ifNull: ['$$soldBy', false] },
                        {
                          id: '$$soldBy._id',
                          firstName: '$$soldBy.firstName',
                          lastName: '$$soldBy.lastName',
                        },
                        null,
                      ],
                    },
                  },
                  null,
                ],
              },
            },
          },
        },
      },

      // Hide payments raw array
      {
        $project: {
          payments: 0,
          salesSoldBy: 0,
          salesProject: 0,
          salesProperty: 0,
          salesPropertyProjects: 0,
          salesPropertyConfigurations: 0,
          salesPropertyAreas: 0,
          salesPropertyCities: 0,
          salesPropertyStates: 0,
          unitBookingOrHoldDoc: 0,
          unitDoc: 0,
          unitProjectDoc: 0,
          unitSoldBy: 0,
          unitPropertyDoc: 0,
          ubohPropertyProjectDoc: 0,
          ubohPropertyConfigurationDoc: 0,
          ubohPropertyLocalityDoc: 0,
          ubohPropertyCityDoc: 0,
          ubohPropertyStateDoc: 0,
          company: 0,
        },
      },
    ];

    return await Customer.paginate(matchFilter, {
      ...options,
      aggregation,
    });
  } catch (_error) {
    console.log('🚀 ~ queryCustomers ~ _error:', _error);
    throw new ApiError(
      defaultStatus.INTERNAL_SERVER_ERROR,
      'Failed to query customers',
      true,
      '',
      CustomerResponseCodes.COMPANY_ERROR,
    );
  }
};

export const updateCustomerDocumentStatus = async ({
  customerId,
  documentId,
  status,
  userId,
  url,
}: {
  customerId: mongoose.Types.ObjectId;
  documentId: mongoose.Types.ObjectId;
  status: CUSTOMER_DOCUMENT_STATUS;
  userId: mongoose.Types.ObjectId;
  url?: string;
}) => {
  const now = new Date();

  const update: any = {
    'documents.$.status': status,
  };

  if (status === CUSTOMER_DOCUMENT_STATUS.GENERATED) {
    update['documents.$.generatedAt'] = now;
    update['documents.$.generatedBy'] = userId;
    update['documents.$.url'] = url;
  }

  if (status === CUSTOMER_DOCUMENT_STATUS.SENT) {
    update['documents.$.sentAt'] = now;
    update['documents.$.sentBy'] = userId;
  }

  // 1️⃣ Try updating existing document entry
  const result = await Customer.updateOne(
    {
      _id: customerId,
      'documents.document': documentId,
    },
    { $set: update },
  );

  // 2️⃣ If document entry does not exist → UPSERT (push)
  if (result.matchedCount === 0) {
    const newDocEntry: any = {
      document: documentId,
      status,
    };

    if (status === CUSTOMER_DOCUMENT_STATUS.GENERATED) {
      newDocEntry.generatedAt = now;
      newDocEntry.generatedBy = userId;
      newDocEntry.url = url;
    }

    if (status === CUSTOMER_DOCUMENT_STATUS.SENT) {
      newDocEntry.sentAt = now;
      newDocEntry.sentBy = userId;
    }

    await Customer.updateOne(
      { _id: customerId },
      { $push: { documents: newDocEntry } },
    );
  }
};

export const cancelCustomerBooking = async (
  customerId: mongoose.Types.ObjectId,
  companyId: mongoose.Types.ObjectId,
  userId: mongoose.Types.ObjectId,
): Promise<{ success: boolean; message: string }> => {
  try {
    const customer = await Customer.findOne({
      _id: customerId,
      company: companyId,
    })
      .populate('unitBookingOrHold')
      .lean();

    if (!customer)
      throw new ApiError(
        defaultStatus.NOT_FOUND,
        'Customer not found',
        true,
        '',
        CustomerResponseCodes.COMPANY_NOT_FOUND,
      );

    const unitBookingOrHoldId = customer.unitBookingOrHold;
    if (!unitBookingOrHoldId)
      throw new ApiError(
        defaultStatus.BAD_REQUEST,
        'No booking found for this customer',
        true,
        '',
        CustomerResponseCodes.COMPANY_ERROR,
      );

    const unitBooking = await UnitBookingOrHold.findById(unitBookingOrHoldId)
      .populate('unit property')
      .lean();

    if (!unitBooking)
      throw new ApiError(
        defaultStatus.NOT_FOUND,
        'Booking record not found',
        true,
        '',
        CustomerResponseCodes.COMPANY_ERROR,
      );

    // Delete payment records for this customer
    await mongoose.model('CustomerPayment').deleteMany({
      customerId,
      company: companyId,
    });

    // Update unit status to available (for builder flow)
    if ((unitBooking as any)?.unit) {
      await mongoose.model('Unit').findByIdAndUpdate(
        (unitBooking as any).unit._id || (unitBooking as any).unit,
        { $set: { status: 'available' } },
      );
    }

    // Update property status to available (for broker flow)
    if ((unitBooking as any)?.property) {
      await IndividualProperties.findByIdAndUpdate(
        (unitBooking as any).property._id || (unitBooking as any).property,
        { $set: { status: 'available', availability: 'Available' } },
      );
    }

    // Delete the UnitBookingOrHold record
    await UnitBookingOrHold.findByIdAndDelete(unitBookingOrHoldId);

    await Customer.findByIdAndUpdate(customerId, {
      $set: {
        bookingStatus: 'cancelled',
        'cancellationDetails.cancelledAt': new Date(),
        'cancellationDetails.cancelledBy': userId,
        unitBookingOrHold: null,
        sales: [],
      },
    });

    // Update lead status from "Lead Won" to "Lead Lost"
    if (customer.leadId) {
      const leadLostStage = await LeadStage.findOne({
        company: companyId,
        stageName: { $regex: /^lead lost$/i },
      }).select('_id');

      if (leadLostStage) {
        await Lead.findByIdAndUpdate(customer.leadId, {
          $set: {
            leadStage: leadLostStage._id,
            isConvertedToCustomer: false,
          },
        });
      }
    }

    return {
      success: true,
      message: 'Booking cancelled successfully. Unit is now available and lead status updated to Lost.',
    };
  } catch (error) {
    if (error instanceof ApiError) throw error;

    throw new ApiError(
      defaultStatus.INTERNAL_SERVER_ERROR,
      'Failed to cancel booking',
      true,
      '',
      CustomerResponseCodes.COMPANY_ERROR,
    );
  }
};
