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

import responseCodes from '@/shared/utils/responseCode/responseCode';
import {
  NewCreatedDocuments,
  UpdateDocumentsBody,
} from '@/modules/documents/documents.interface';
import { getObjectId } from '@/shared/utils/commonHelper';
import { safeDeleteById } from '@/shared/utils/guard/ref-guard';

const { DocumentsResponseCodes } = responseCodes;

export const createDocuments = async (
  data: NewCreatedDocuments,
): Promise<boolean> => {
  const documents = await Documents.create(data);

  if (!documents)
    throw new ApiError(
      defaultStatus.OK,
      'Failed to create documents',
      true,
      '',
      DocumentsResponseCodes.DOCUMENTS_ERROR,
    );

  return true;
};

export const getDocumentsById = async (id: string) => {
  const documents = await Documents.findById(id);
  if (!documents)
    throw new ApiError(
      defaultStatus.OK,
      'Documents not found',
      true,
      '',
      DocumentsResponseCodes.DOCUMENTS_NOT_FOUND,
    );

  return documents;
};

export const updateDocuments = async (
  id: string,
  updateData: UpdateDocumentsBody,
): Promise<boolean | null> => {
  try {
    const result = await Documents.updateOne(
      { _id: getObjectId(id) },
      { $set: updateData },
    );

    if (result.matchedCount === 0)
      throw new ApiError(
        defaultStatus.OK,
        'Documents not found',
        false,
        '',
        DocumentsResponseCodes.DOCUMENTS_NOT_FOUND,
      );

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

export const deleteDocuments = async (id: string): Promise<void> => {
  try {
    await safeDeleteById(
      Documents,
      id,
      DocumentsResponseCodes.DOCUMENTS_IN_USE,
    );
  } catch (err: unknown) {
    if (err instanceof ApiError) throw err;

    throw new ApiError(
      defaultStatus.OK,
      'Failed to delete documents',
      true,
      '',
      DocumentsResponseCodes.DOCUMENTS_ERROR,
    );
  }
};

export const queryDocuments = async (
  filter: Record<string, string> = {},
  options = {},
) => {
  const { search, project, status, companyId, ...rest } = filter;

  const query: Record<string, unknown> = {
    ...rest,
    ...(search && {
      name: { $regex: search, $options: 'i' },
    }),
    ...(status && {
      status,
    }),
    ...(companyId && {
      companyId: getObjectId(companyId),
    }),
  };

  if (project) {
    let projectObjectId;
    try {
      projectObjectId = getObjectId(project);
    } catch {
      projectObjectId = undefined;
    }

    const projectMatchOr: Record<string, unknown>[] = [
      { projectIds: project },
      { 'projectIds.id': project },
      { 'projectIds._id': project },
    ];

    if (projectObjectId) {
      projectMatchOr.push(
        { projectIds: projectObjectId },
        { 'projectIds.id': projectObjectId },
        { 'projectIds._id': projectObjectId },
      );
    }

    query.$or = [
      { scope: 'ALL_PROJECTS' },
      {
        scope: 'SPECIFIC_PROJECTS',
        $or: projectMatchOr,
      },
    ];

    if (process.env.NODE_ENV === 'development') {
      console.debug('[documents] queryDocuments project filter query:', query);
    }
  }

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