import { ApiError } from '@/shared/utils/errors';
import { defaultStatus } from '@/shared/utils/responseCode/httpStatusAlias';
import responseCodes from '@/shared/utils/responseCode/responseCode';
import { getObjectId } from '@/shared/utils/commonHelper';
import { CPCompany, CPContact, CPPayment } from './channelPartner.model';
import {
  NewCPCompany,
  UpdateCPCompanyBody,
  NewCPContact,
  UpdateCPContactBody,
  NewCPPayment,
  QueryFilter,
  ICPCompanyDoc,
  ICPContactDoc,
  ICPPaymentDoc,
} from './channelPartner.interface';
import { PaginateOptions } from '@/shared/utils/plugins/paginate/paginate';
import { Types, FilterQuery } from 'mongoose';
import { UnitBookingOrHold } from '@/modules/activity/unitBookingOrHold/unitBookingOrHold.model';

const { ChannelPartnerResponseCodes } = responseCodes;

// ===================== CP Company =====================

export const createCPCompany = async (
  data: NewCPCompany & { primaryContacts?: any[] },
): Promise<ICPCompanyDoc> => {
  const existing = await CPCompany.findOne({
    company: data.company,
    companyName: data.companyName,
  });

  if (existing)
    throw new ApiError(
      defaultStatus.BAD_REQUEST,
      'Channel partner company with this name already exists',
      true,
      '',
      ChannelPartnerResponseCodes.CHANNELPARTNER_ALREADY_EXISTS,
    );

  const { primaryContacts, ...companyData } = data;
  const cpCompany = await CPCompany.create(companyData);

  if (primaryContacts && primaryContacts.length > 0) {
    const contactDocs = primaryContacts.map((contact, index) => ({
      firstName: contact.firstName,
      lastName: contact.lastName || '',
      phone: contact.phone || [
        {
          countryCode: parseInt(
            contact.countryCode?.replace('+', '') || '91',
            10,
          ),
          number: parseInt(contact.phone, 10),
          isPrimary: true,
        },
      ],
      email: contact.email || '',
      isPrimary: index === 0 || contact.isPrimary === true,
      status: 'Active' as const,
      cpCompany: cpCompany._id,
      company: data.company,
      createdBy: data.createdBy,
      updatedBy: data.updatedBy,
    }));

    await CPContact.insertMany(contactDocs);
  }

  return cpCompany;
};

export const getCPCompanyById = async (id: string): Promise<ICPCompanyDoc> => {
  const cpCompany = await CPCompany.findById(id).populate([
    { path: 'createdBy', select: 'firstName lastName' },
    { path: 'updatedBy', select: 'firstName lastName' },
  ]);

  if (!cpCompany)
    throw new ApiError(
      defaultStatus.OK,
      'Channel partner company not found',
      true,
      '',
      ChannelPartnerResponseCodes.CHANNELPARTNER_NOT_FOUND,
    );

  const contactCount = await CPContact.countDocuments({
    cpCompany: cpCompany._id,
    status: 'Active',
  });

  const primaryContact = await CPContact.findOne({
    cpCompany: cpCompany._id,
    isPrimary: true,
  });

  const payments = await CPPayment.aggregate([
    { $match: { cpCompany: cpCompany._id } },
    {
      $group: {
        _id: null,
        totalPaid: { $sum: '$amountPaid' },
        lastPaymentDate: { $max: '$paymentDate' },
      },
    },
  ]);

  const totalPaid = payments[0]?.totalPaid || 0;
  const lastPaymentDate = payments[0]?.lastPaymentDate || null;

  const cpContactIds = await CPContact.find({
    cpCompany: cpCompany._id,
  }).distinct('_id');

  let totalUnitsSold = 0;
  let totalSalesValue = 0;

  if (cpContactIds.length > 0) {
    const salesAgg = await UnitBookingOrHold.aggregate([
      { $match: { cpContact: { $in: cpContactIds }, action: 'book' } },
      {
        $group: {
          _id: null,
          count: { $sum: 1 },
          totalValue: { $sum: '$bookingAmount' },
        },
      },
    ]);

    totalUnitsSold = salesAgg[0]?.count || 0;
    totalSalesValue = salesAgg[0]?.totalValue || 0;
  }

  const baseRate = cpCompany.baseCommissionRate || 0;
  const totalCommissionGenerated =
    baseRate > 0 ? (totalSalesValue * baseRate) / 100 : 0;

  const result = cpCompany.toObject();
  return {
    ...result,
    contactCount,
    primaryContactName: primaryContact
      ? `${primaryContact.firstName} ${primaryContact.lastName || ''}`.trim()
      : null,
    primaryContactPhone: primaryContact?.phone?.[0]
      ? `+${primaryContact.phone[0].countryCode} ${primaryContact.phone[0].number}`
      : null,
    primaryContactEmail: primaryContact?.email || null,
    totalCommissionPaid: totalPaid,
    totalCommissionGenerated,
    totalUnitsSold,
    totalSalesValue,
    lastPaymentDate,
  } as any;
};

export const queryCPCompanies = async (
  rawFilter: QueryFilter,
  options: PaginateOptions,
) => {
  const filter: FilterQuery<ICPCompanyDoc> = {};

  if (rawFilter.companyId) filter.company = getObjectId(rawFilter.companyId);

  if (rawFilter.status) filter.status = rawFilter.status;

  if (rawFilter.search) {
    const searchRegex = new RegExp(rawFilter.search, 'i');
    filter.$or = [
      { companyName: searchRegex },
      { city: searchRegex },
      { address: searchRegex },
    ];
  }

  const results = await CPCompany.paginate(filter, options);
  const docs = results.results || results.docs || [];

  const enriched = await Promise.all(
    docs.map(async (doc: any) => {
      const cpId = doc._id || doc.id;

      const contactCount = await CPContact.countDocuments({
        cpCompany: cpId,
        status: 'Active',
      });

      const primaryContact = await CPContact.findOne({
        cpCompany: cpId,
        isPrimary: true,
      });

      const payments = await CPPayment.aggregate([
        { $match: { cpCompany: new Types.ObjectId(cpId) } },
        {
          $group: {
            _id: null,
            totalPaid: { $sum: '$amountPaid' },
          },
        },
      ]);

      const obj = typeof doc.toObject === 'function' ? doc.toObject() : doc;
      return {
        ...obj,
        contactCount,
        primaryContactName: primaryContact
          ? `${primaryContact.firstName} ${primaryContact.lastName || ''}`.trim()
          : null,
        primaryContactPhone: primaryContact?.phone?.[0]
          ? `+${primaryContact.phone[0].countryCode} ${primaryContact.phone[0].number}`
          : null,
        totalCommissionPaid: payments[0]?.totalPaid || 0,
      };
    }),
  );

  return { ...results, results: enriched, docs: enriched };
};

export const updateCPCompany = async (
  id: string,
  updateData: UpdateCPCompanyBody,
): Promise<ICPCompanyDoc | null> => {
  const cpCompany = await CPCompany.findById(id);

  if (!cpCompany)
    throw new ApiError(
      defaultStatus.OK,
      'Channel partner company not found',
      true,
      '',
      ChannelPartnerResponseCodes.CHANNELPARTNER_NOT_FOUND,
    );

  Object.assign(cpCompany, updateData);
  await cpCompany.save();
  return cpCompany;
};

export const deleteCPCompany = async (id: string): Promise<boolean> => {
  const cpCompany = await CPCompany.findById(id);

  if (!cpCompany)
    throw new ApiError(
      defaultStatus.OK,
      'Channel partner company not found',
      true,
      '',
      ChannelPartnerResponseCodes.CHANNELPARTNER_NOT_FOUND,
    );

  await CPContact.deleteMany({ cpCompany: cpCompany._id });
  await CPPayment.deleteMany({ cpCompany: cpCompany._id });
  await cpCompany.deleteOne();
  return true;
};

export const bulkUpdateCPCompanyStatus = async (
  ids: string[],
  status: 'Active' | 'Inactive',
): Promise<number> => {
  const result = await CPCompany.updateMany(
    { _id: { $in: ids.map((id) => getObjectId(id)) } },
    { $set: { status } },
  );
  return result.modifiedCount;
};

export const bulkDeleteCPCompanies = async (ids: string[]): Promise<number> => {
  const objectIds = ids.map((id) => getObjectId(id));
  await CPContact.deleteMany({ cpCompany: { $in: objectIds } });
  await CPPayment.deleteMany({ cpCompany: { $in: objectIds } });
  const result = await CPCompany.deleteMany({ _id: { $in: objectIds } });
  return result.deletedCount;
};

// ===================== CP Contact =====================

export const createCPContacts = async (
  cpCompanyId: string | Types.ObjectId,
  contacts: Array<
    Omit<NewCPContact, 'cpCompany' | 'company'> & { company?: Types.ObjectId }
  >,
  companyId: Types.ObjectId,
  createdBy?: Types.ObjectId,
): Promise<ICPContactDoc[]> => {
  const cpCompany = await CPCompany.findById(cpCompanyId);
  if (!cpCompany)
    throw new ApiError(
      defaultStatus.OK,
      'Channel partner company not found',
      true,
      '',
      ChannelPartnerResponseCodes.CHANNELPARTNER_NOT_FOUND,
    );

  const hasPrimary = contacts.some((c) => c.isPrimary === true);
  if (hasPrimary)
    await CPContact.updateMany(
      { cpCompany: cpCompany._id, isPrimary: true },
      { $set: { isPrimary: false } },
    );

  const contactDocs = contacts.map((contact) => ({
    ...contact,
    cpCompany: cpCompany._id,
    company: companyId,
    createdBy,
    updatedBy: createdBy,
  }));

  const created = await CPContact.insertMany(contactDocs);
  return created as ICPContactDoc[];
};

export const queryCPContacts = async (
  rawFilter: QueryFilter,
  options: PaginateOptions,
) => {
  const filter: FilterQuery<ICPContactDoc> = {};

  if (rawFilter.cpCompany) filter.cpCompany = getObjectId(rawFilter.cpCompany);

  if (rawFilter.companyId) filter.company = getObjectId(rawFilter.companyId);

  if (rawFilter.status) filter.status = rawFilter.status;

  if (rawFilter.search) {
    const searchRegex = new RegExp(rawFilter.search, 'i');
    filter.$or = [
      { firstName: searchRegex },
      { lastName: searchRegex },
      { email: searchRegex },
    ];
  }

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

export const updateCPContact = async (
  id: string,
  updateData: UpdateCPContactBody,
): Promise<ICPContactDoc | null> => {
  const contact = await CPContact.findById(id);

  if (!contact)
    throw new ApiError(
      defaultStatus.OK,
      'Channel partner contact not found',
      true,
      '',
      ChannelPartnerResponseCodes.CHANNELPARTNER_NOT_FOUND,
    );

  if (updateData.isPrimary === true)
    await CPContact.updateMany(
      { cpCompany: contact.cpCompany, _id: { $ne: contact._id } },
      { $set: { isPrimary: false } },
    );

  Object.assign(contact, updateData);
  await contact.save();
  return contact;
};

export const deleteCPContact = async (id: string): Promise<boolean> => {
  const contact = await CPContact.findById(id);

  if (!contact)
    throw new ApiError(
      defaultStatus.OK,
      'Channel partner contact not found',
      true,
      '',
      ChannelPartnerResponseCodes.CHANNELPARTNER_NOT_FOUND,
    );

  await contact.deleteOne();
  return true;
};

// ===================== CP Payment =====================

export const recordPayment = async (data: NewCPPayment): Promise<any> => {
  const cpCompany = await CPCompany.findById(data.cpCompany);

  if (!cpCompany)
    throw new ApiError(
      defaultStatus.OK,
      'Channel partner company not found',
      true,
      '',
      ChannelPartnerResponseCodes.CHANNELPARTNER_NOT_FOUND,
    );

  const payment = await CPPayment.create(data);
  return payment;
};

export const queryPayments = async (
  rawFilter: QueryFilter,
  options: PaginateOptions,
) => {
  const filter: FilterQuery<ICPPaymentDoc> = {};

  if (rawFilter.cpCompany) filter.cpCompany = getObjectId(rawFilter.cpCompany);

  if (rawFilter.companyId) filter.company = getObjectId(rawFilter.companyId);

  return CPPayment.paginate(filter, {
    ...options,
    sortBy: options.sortBy || 'paymentDate:desc',
  } as PaginateOptions);
};

// ===================== CP Sales =====================

export const queryCPSales = async (
  rawFilter: QueryFilter & { search?: string },
  options: PaginateOptions,
) => {
  if (!rawFilter.cpCompany)
    throw new ApiError(
      defaultStatus.BAD_REQUEST,
      'cpCompany is required',
      true,
      '',
      ChannelPartnerResponseCodes.CHANNELPARTNER_NOT_FOUND,
    );

  const cpContactIds = await CPContact.find({
    cpCompany: getObjectId(rawFilter.cpCompany),
  }).distinct('_id');

  if (cpContactIds.length === 0)
    return {
      results: [],
      page: Number(options.page) || 1,
      limit: Number(options.limit) || 10,
      totalPages: 0,
      totalResults: 0,
    };

  const filter: FilterQuery<any> = {
    cpContact: { $in: cpContactIds },
    action: 'book',
  };

  const paginateOptions: PaginateOptions = {
    ...options,
    sortBy: options.sortBy || 'createdAt:desc',
    populate:
      'lead:contactDetails;project:projectName;unit:unitNumber;soldBy:firstName,lastName;cpContact:firstName,lastName,cpCompany',
  };

  const result = await UnitBookingOrHold.paginate(filter, paginateOptions);

  const docs = result.results || result.docs || [];

  const cpCompanyDoc = await CPCompany.findById(
    getObjectId(rawFilter.cpCompany),
  ).select('baseCommissionRate companyName');

  const baseRate = cpCompanyDoc?.baseCommissionRate || 0;

  const enriched = docs.map((doc: any) => {
    const obj = typeof doc.toObject === 'function' ? doc.toObject() : doc;
    const bookingAmount = obj.bookingAmount || 0;
    const commissionAmount =
      baseRate > 0 ? (bookingAmount * baseRate) / 100 : 0;

    return {
      ...obj,
      commissionRate: baseRate,
      commissionAmount,
      cpCompanyName: cpCompanyDoc?.companyName || '',
    };
  });

  return { ...result, results: enriched, docs: enriched };
};
