import httpStatus from 'http-status';
import { FilterQuery, PipelineStage } from 'mongoose';

import City from '@/modules/master/location/city/city.model.js';
import ApiError from '@/shared/utils/errors/ApiError.js';
import { ICity } from '@/modules/master/location/location.interfaces.js';
import pick from '@/shared/utils/pick.js';
import { getObjectId } from '@/shared/utils/commonHelper';
import { defaultStatus } from '@/shared/utils/responseCode/httpStatusAlias';
import responseCodes from '@/shared/utils/responseCode/responseCode';
import { safeDeleteById } from '@/shared/utils/guard/ref-guard';
const { CityResponseCodes } = responseCodes;

export const create = async (data: Partial<ICity>): Promise<ICity> => {
  const { name, state, loc } = data;

  const existing = await City.findOne({
    name,
    state,
    'loc.coordinates': loc?.coordinates,
  });

  if (existing)
    throw new ApiError(
      defaultStatus.BAD_REQUEST,
      'City with this name and coordinates already exists.',
      true,
      '',
      CityResponseCodes.CITY_ALREADY_EXISTS,
    );

  return await City.create(data);
};

export const getById = async (id: string): Promise<ICity> => {
  const city = await City.findById(id);
  if (!city) throw new ApiError(httpStatus.NOT_FOUND, 'City not found');
  return city;
};

export const update = async (
  id: string,
  data: Partial<ICity>,
): Promise<ICity> => {
  const city = await City.findByIdAndUpdate(id, data, {
    new: true,
    runValidators: true,
  });
  if (!city) throw new ApiError(httpStatus.NOT_FOUND, 'City not found');
  return city;
};

export const remove = async (id: string): Promise<boolean> => {
  await safeDeleteById(City, id, CityResponseCodes.CITY_IN_USE);
  return true;
};

export const list = async (query: Record<string, unknown>) => {
  const filter = pick(query, ['search', 'state']);
  const withPropertiesOnly = query.withPropertiesOnly === 'true' || query.withPropertiesOnly === true;

  const options = pick(query, [
    'sortBy',
    'limit',
    'page',
    'populate',
    'fields',
  ]);
  let queryFilter: FilterQuery<ICity> = {
    ...(filter.state && { state: getObjectId(filter.state) }),
  };

  if (filter.search)
    queryFilter.$or = [{ name: { $regex: filter.search, $options: 'i' } }];

  const pipeline: PipelineStage[] = [
    {
      $match: queryFilter,
    },
    {
      $lookup: {
        from: 'states',
        localField: 'state',
        foreignField: '_id',
        as: 'state',
      },
    },
    { $unwind: '$state' },
  ];

  if (withPropertiesOnly) {
    pipeline.push(
      {
        $lookup: {
          from: 'individualproperties',
          localField: '_id',
          foreignField: 'city',
          as: 'properties',
        },
      },
      {
        $addFields: {
          hasProperties: { $gt: [{ $size: '$properties' }, 0] },
        },
      },
      {
        $match: {
          hasProperties: true,
        },
      },
      {
        $project: {
          properties: 0,
          hasProperties: 0,
        },
      }
    );
  }

  options.aggregation = pipeline;

  return await City.paginate(queryFilter, options);
};
