import mongoose from 'mongoose';
import Project from './project.model';
import { getObjectId } from '@/shared/utils/commonHelper';

export const normalizeSearchQuery = (query: string) =>
  query.trim().toLowerCase().replace(/\s+/g, ' ');

// TODO: remove all and find better way
export const findSimilarProjects = async (searchQuery, location) => {
  const normalized = normalizeSearchQuery(searchQuery);

  const exactMatch = await Project.findOne({
    normalizedName: normalized,
    city: getObjectId(location?.city),
    state: getObjectId(location?.state),
    locality: getObjectId(location?.area),
  });
  if (exactMatch) return { exact: exactMatch };
  return { exact: null };
};

export const buildSearchMatchConditions = (searchValue: string) => {
  const searchRegex = { $regex: searchValue, $options: 'i' };

  const conditions = [
    { projectName: searchRegex },
    { reraId: searchRegex },
    { landmark: searchRegex },
    { 'localityData.name': searchRegex },
    { 'cityData.name': searchRegex },
    { 'stateData.name': searchRegex },
    { 'propertyTypeData.name': searchRegex },
    {
      $expr: {
        $regexMatch: {
          input: { $toString: '$pricePerSqYard' },
          regex: searchValue,
          options: 'i',
        },
      },
    },
  ];

  return conditions;
};
export const buildPriceRangeFilter = (priceRange: string): object => {
  switch (priceRange) {
    case 'under_50L':
      return {
        $or: [{ pricePerSqYard: { $lt: 5000000 } }],
      };
    case '50L_1Cr':
      return {
        $or: [{ pricePerSqYard: { $gte: 5000000, $lte: 10000000 } }],
      };
    case 'above_1Cr':
      return {
        $or: [{ pricePerSqYard: { $gt: 10000000 } }],
      };
    default:
      return {};
  }
};
export const processFilter = (
  filter: Record<string, unknown>,
): Record<string, unknown> => {
  const parseIds = (value?: string) => {
    if (!value) return undefined;

    const ids = value
      .split(',')
      .map((id) => new mongoose.Types.ObjectId(id.trim()));
    return ids.length === 1 ? ids[0] : { $in: ids };
  };

  const parseStrings = (value?: string) => {
    if (!value) return undefined;

    const values = value.split(',').map((v) => v.trim());
    return values.length === 1 ? values[0] : { $in: values };
  };

  const updatedFilter = { ...filter };

  delete updatedFilter.search;

  if (updatedFilter.price) {
    const priceRange = updatedFilter.price as string;
    delete updatedFilter.price;

    if (!Array.isArray(updatedFilter.$and)) updatedFilter.$and = [];

    const priceFilter = buildPriceRangeFilter(priceRange);
    if (Object.keys(priceFilter).length > 0)
      (updatedFilter.$and as object[]).push(priceFilter);
  }

  if (updatedFilter.companyId && typeof updatedFilter.companyId === 'string')
    if (updatedFilter.companyId === 'global') delete updatedFilter.companyId;
    else
      updatedFilter.companyId = new mongoose.Types.ObjectId(
        updatedFilter.companyId,
      );

  if (updatedFilter.propertyType && typeof updatedFilter.propertyType === 'string') {
    updatedFilter.propertyType = parseIds(updatedFilter.propertyType);
  }

  if (updatedFilter.status && typeof updatedFilter.status === 'string') {
    updatedFilter.status = parseStrings(updatedFilter.status);
  }

  return updatedFilter;
};
export const createLookupStages = () => [
  {
    $lookup: {
      from: 'areas',
      localField: 'locality',
      foreignField: '_id',
      as: 'localityData',
    },
  },
  {
    $lookup: {
      from: 'users',
      localField: 'createdBy',
      foreignField: '_id',
      as: 'createdByData',
    },
  },
  {
    $lookup: {
      from: 'cities',
      localField: 'city',
      foreignField: '_id',
      as: 'cityData',
    },
  },
  {
    $lookup: {
      from: 'categories',
      localField: 'propertyType',
      foreignField: '_id',
      as: 'propertyTypeData',
    },
  },
];
export const createAddFieldsStage = () => ({
  $addFields: {
    'locality.name': { $arrayElemAt: ['$localityData.name', 0] },
    'city.name': { $arrayElemAt: ['$cityData.name', 0] },
    'propertyType.name': { $arrayElemAt: ['$propertyTypeData.name', 0] },
    createdBy: {
      firstName: { $arrayElemAt: ['$createdByData.firstName', 0] },
      lastName: { $arrayElemAt: ['$createdByData.lastName', 0] },
      email: { $arrayElemAt: ['$createdByData.email', 0] },
      _id: { $arrayElemAt: ['$createdByData._id', 0] },
    },
  },
});
function buildFields(fields: string): Record<string, 1 | 0> {
  if (!fields) return {};
  const project: Record<string, 1 | 0> = {};
  fields?.split(' ').forEach((field) => {
    project[field] = 1;
  });
  return project;
}

export const buildSearchPipeline = (
  filter: Record<string, unknown>,
  searchValue: string,
  fields: string,
) => {
  const processedFilter = processFilter(filter);

  return [
    { $match: processedFilter },
    ...createLookupStages(),
    {
      $match: {
        $or: buildSearchMatchConditions(searchValue),
      },
    },
    createAddFieldsStage(),
    {
      $project: buildFields(fields),
    },
  ];
};
