import uniq from "lodash/uniq";
import { Op, QueryTypes } from "sequelize";
import Logger from "@server/logging/Logger";
import { Document, Attachment } from "@server/models";
import { DocumentHelper } from "@server/models/helpers/DocumentHelper";
import { ProsemirrorHelper } from "@server/models/helpers/ProsemirrorHelper";
import DeleteAttachmentTask from "@server/queues/tasks/DeleteAttachmentTask";
import { sequelize } from "@server/storage/database";

export default async function documentPermanentDeleter(documents: Document[]) {
  const activeDocument = documents.find((doc) => !doc.deletedAt);

  if (activeDocument) {
    throw new Error(
      `Cannot permanently delete ${activeDocument.id} document. Please delete it and try again.`
    );
  }

  const query = `
    SELECT COUNT(id)
    FROM documents
    WHERE "searchVector" @@ to_tsquery('english', :query) AND
    "teamId" = :teamId AND
    "id" != :documentId
  `;

  for (const document of documents) {
    // Find any attachments that are referenced in the text content
    const attachmentIdsInText = ProsemirrorHelper.parseAttachmentIds(
      DocumentHelper.toProsemirror(document)
    );

    // Find any attachments that were originally uploaded to this document
    const attachmentIdsForDocument = (
      await Attachment.findAll({
        attributes: ["id"],
        where: {
          teamId: document.teamId,
          documentId: document.id,
        },
      })
    ).map((attachment) => attachment.id);

    const attachmentIds = uniq([
      ...attachmentIdsInText,
      ...attachmentIdsForDocument,
    ]);

    await Promise.all(
      attachmentIds.map(async (attachmentId) => {
        // Check if the attachment is referenced in any other documents – this
        // is needed as it's easy to copy and paste content between documents.
        // An uploaded attachment may end up referenced in multiple documents.
        const [{ count }] = await sequelize.query<{ count: string }>(query, {
          type: QueryTypes.SELECT,
          replacements: {
            documentId: document.id,
            teamId: document.teamId,
            query: attachmentId,
          },
        });

        // If the attachment is not referenced in any other documents then
        // delete it from the database and the storage provider.
        if (parseInt(count) === 0) {
          Logger.info(
            "commands",
            `Attachment ${attachmentId} scheduled for deletion`
          );
          await DeleteAttachmentTask.schedule({
            attachmentId,
            teamId: document.teamId,
          });
        }
      })
    );
  }

  const documentIds = documents.map((document) => document.id);
  await Document.update(
    {
      parentDocumentId: null,
    },
    {
      where: {
        parentDocumentId: {
          [Op.in]: documentIds,
        },
      },
      paranoid: false,
    }
  );

  return Document.scope("withDrafts").destroy({
    where: {
      id: documents.map((document) => document.id),
    },
    force: true,
  });
}
