import fs from 'node:fs'
import FileWriter from '../../infrastructure/FileWriter.mjs'
import EditorController from '../Editor/EditorController.mjs'
import ProjectLocator from '../Project/ProjectLocator.mjs'
import { Project } from '../../models/Project.mjs'
import ProjectGetter from '../Project/ProjectGetter.mjs'
import LinkedFilesErrors from './LinkedFilesErrors.mjs'
import { callbackifyAll } from '@overleaf/promise-utils'

const { ProjectNotFoundError, V1ProjectNotFoundError, BadDataError } =
  LinkedFilesErrors

const LinkedFilesHandler = {
  async getFileById(projectId, fileId) {
    const { element, path, folder } = await ProjectLocator.promises.findElement(
      {
        project_id: projectId,
        element_id: fileId,
        type: 'file',
      }
    )
    return { file: element, path, parentFolder: folder }
  },

  async getSourceProject(data) {
    const projection = { _id: 1, name: 1, overleaf: 1 } // include the historyId for future use
    if (data.v1_source_doc_id != null) {
      const project = await Project.findOne(
        { 'overleaf.id': data.v1_source_doc_id },
        projection
      ).exec()

      if (project == null) {
        throw new V1ProjectNotFoundError()
      }

      return project
    } else if (data.source_project_id != null) {
      const project = await ProjectGetter.promises.getProject(
        data.source_project_id,
        projection
      )

      if (project == null) {
        throw new ProjectNotFoundError()
      }

      return project
    } else {
      throw new BadDataError('neither v1 nor v2 id present')
    }
  },

  async importFromStream(
    projectId,
    readStream,
    linkedFileData,
    name,
    parentFolderId,
    userId
  ) {
    const fsPath = await FileWriter.promises.writeStreamToDisk(
      projectId + '_linked-files-handler-stream',
      readStream
    )

    try {
      return await EditorController.promises.upsertFile(
        projectId,
        parentFolderId,
        name,
        fsPath,
        linkedFileData,
        'upload',
        userId
      )
    } finally {
      await fs.promises.unlink(fsPath).catch(() => {})
    }
  },

  async importContent(
    projectId,
    content,
    linkedFileData,
    name,
    parentFolderId,
    userId
  ) {
    const fsPath = await FileWriter.promises.writeContentToDisk(
      projectId + '_linked-files-handler-content',
      content
    )

    try {
      return await EditorController.promises.upsertFile(
        projectId,
        parentFolderId,
        name,
        fsPath,
        linkedFileData,
        'upload',
        userId
      )
    } finally {
      await fs.promises.unlink(fsPath).catch(() => {})
    }
  },
}

export default {
  promises: LinkedFilesHandler,
  ...callbackifyAll(LinkedFilesHandler, {
    multiResult: { getFileById: ['file', 'path', 'parentFolder'] },
  }),
}
