import { Types } from 'mongoose';

import { ApiError } from '@/shared/utils/errors';
import { defaultStatus } from '@/shared/utils/responseCode/httpStatusAlias';
import { CustomFields } from '@/modules/customFields/customFields.model';

import responseCodes from '@/shared/utils/responseCode/responseCode';
import {
  ICustomFieldsOption,
  ICustomFieldsOptionDoc,
  ICustomFieldsDoc,
  NewCreatedCustomFields,
  UpdateCustomFieldsBody,
} from '@/modules/customFields/customFields.interface';
import { getObjectId } from '@/shared/utils/commonHelper';

const { CustomFieldsResponseCodes } = responseCodes;

export const createCustomFields = async (
  data: NewCreatedCustomFields,
): Promise<boolean> => {
  const { companyId, formName, key, sequence, section } = data;

  const [isDuplicateKey, isDuplicateSequence] = await Promise.all([
    CustomFields.findOne({ companyId, formName, key }),
    sequence
      ? CustomFields.findOne({ companyId, formName, section, sequence })
      : Promise.resolve(null),
  ]);

  if (isDuplicateKey)
    throw new ApiError(
      defaultStatus.CONFLICT,
      `Custom field key "${key}" already exists for this form.`,
      true,
      '',
      CustomFieldsResponseCodes.CUSTOMFIELDS_ALREADY_EXISTS,
    );

  if (isDuplicateSequence)
    throw new ApiError(
      defaultStatus.CONFLICT,
      `Custom field with sequence "${sequence}" already exists for this form.`,
      true,
      '',
      CustomFieldsResponseCodes.CUSTOMFIELDS_SEQUENCE_EXISTS,
    );

  const customFields = await CustomFields.create(data);

  if (!customFields)
    throw new ApiError(
      defaultStatus.OK,
      'Failed to create customFields',
      true,
      '',
      CustomFieldsResponseCodes.CUSTOMFIELDS_ERROR,
    );

  return true;
};

export const getCustomFieldsById = async (id: string) => {
  const customFields = await CustomFields.findById(id);
  if (!customFields)
    throw new ApiError(
      defaultStatus.OK,
      'CustomFields not found',
      true,
      '',
      CustomFieldsResponseCodes.CUSTOMFIELDS_NOT_FOUND,
    );

  return customFields;
};

export const updateCustomFields = async (
  id: string,
  updateData: UpdateCustomFieldsBody,
): Promise<boolean | null> => {
  try {
    const field = await CustomFields.findById(getObjectId(id));
    if (!field)
      throw new ApiError(
        defaultStatus.OK,
        'CustomFields not found',
        false,
        '',
        CustomFieldsResponseCodes.CUSTOMFIELDS_NOT_FOUND,
      );

    if ('options' in updateData) {
      const incoming: ICustomFieldsOption[] = updateData.options || [];

      const existing = (field.options || []).map((opt) =>
        (opt as ICustomFieldsOptionDoc).toObject(),
      );

      const incomingMap = new Map<string, ICustomFieldsOption>(
        incoming.map((opt) => [opt.value, { ...opt, isDeleted: false }]),
      );

      for (const existingOpt of existing)
        if (!incomingMap.has(existingOpt.value))
          incomingMap.set(existingOpt.value, {
            ...existingOpt,
            isDeleted: true,
          });

      updateData.options = Array.from(incomingMap.values());
    }

    const result = await CustomFields.updateOne(
      { _id: field._id },
      { $set: updateData },
      { new: true },
    );

    if (result.matchedCount === 0)
      throw new ApiError(
        defaultStatus.OK,
        'CustomFields not found',
        false,
        '',
        CustomFieldsResponseCodes.CUSTOMFIELDS_NOT_FOUND,
      );

    return true;
  } catch (err: unknown) {
    if (err instanceof ApiError) throw err;
    throw new ApiError(
      defaultStatus.OK,
      'Failed to update customFields',
      true,
      '',
      CustomFieldsResponseCodes.CUSTOMFIELDS_ERROR,
    );
  }
};

export const deleteCustomFields = async (id: string): Promise<void> => {
  try {
    const deletedCustomField = await CustomFields.findByIdAndDelete(getObjectId(id));

    if (!deletedCustomField) 
      throw new ApiError(
        defaultStatus.NOT_FOUND,
        'CustomFields not found',
        false,
        '',
        CustomFieldsResponseCodes.CUSTOMFIELDS_NOT_FOUND,
      );
    
  } catch (err: unknown) {
    if (err instanceof ApiError) throw err;

    throw new ApiError(
      defaultStatus.OK,
      'Failed to delete customFields',
      true,
      '',
      CustomFieldsResponseCodes.CUSTOMFIELDS_ERROR,
    );
  }
};


export const queryCustomFields = async (
  filter: Record<string, string> = {},
  options = {},
) => {
  const { search, isDeleted, ...rest } = filter;

  const query: Record<string, unknown> = {
    ...rest,
    isDeleted: isDeleted ?? false,
    ...(filter.companyId && { companyId: getObjectId(filter.companyId) }),
    ...(search && {
      formName: { $regex: search, $options: 'i' },
    }),
  };

  return CustomFields.paginate(query, options);
};

export const updateCustomFieldsSequence = async ({
  companyId,
  formName,
  sequenceUpdates,
}) => {
  try {
    if (!companyId || !getObjectId(companyId))
      throw new ApiError(
        defaultStatus.BAD_REQUEST,
        'companyId must be a valid ObjectId',
        true,
        '',
        CustomFieldsResponseCodes.INVALID_INPUT,
      );

    const customFields = await CustomFields.find({
      companyId: getObjectId(companyId),
      formName,
    });

    if (customFields.length === 0)
      throw new ApiError(
        defaultStatus.NOT_FOUND,
        'CustomFields not found',
        true,
        '',
        CustomFieldsResponseCodes.CUSTOMFIELDS_NOT_FOUND,
      );

    if (!Array.isArray(sequenceUpdates) || sequenceUpdates.length === 0)
      throw new ApiError(
        defaultStatus.BAD_REQUEST,
        'sequenceUpdates must be a non-empty array',
        true,
        '',
        CustomFieldsResponseCodes.INVALID_INPUT,
      );

    const invalidIds = sequenceUpdates.filter(
      (item) =>
        !item.id ||
        !Types.ObjectId.isValid(item.id) ||
        typeof item.sequence !== 'number',
    );

    if (invalidIds.length > 0)
      throw new ApiError(
        defaultStatus.BAD_REQUEST,
        'One or more invalid IDs or sequences found',
        true,
        '',
        CustomFieldsResponseCodes.INVALID_CUSTOM_FIELD_ID,
      );

    await Promise.all(
      sequenceUpdates.map(({ id, sequence }) =>
        CustomFields.updateOne({ _id: id }, { $set: { sequence } }),
      ),
    );

    return true;
  } catch (err: unknown) {
    if (err instanceof ApiError) throw err;
    throw new ApiError(
      defaultStatus.OK,
      'Failed to update customFields',
      true,
      '',
      CustomFieldsResponseCodes.CUSTOMFIELDS_ERROR,
    );
  }
};

export const getCustomFieldsByFormName = async (
  companyId: Types.ObjectId | undefined,
  formName: string,
): Promise<Record<string, unknown>[]> => {
  const query: Record<string, unknown> = {
    formName,
    isDeleted: false,
  };

  if (companyId) {
    query.companyId = companyId;
  }

  return CustomFields.find(query).sort({ sequence: 1 }).lean();
};
