import { IResolvers } from 'apollo-server-express'
import fs from 'fs'
import randombytes from 'randombytes'
import sharp from 'sharp'
import { tmpDir, uploadDir, thumbnails } from './constants'
import user from '../user'

export const resolvers: IResolvers = {
  Query: {
    fsFiles: async (parent, { directory }, context, info) => {
      user.checkPermission(context, 'ADMIN')
      const data = await fsFiles(directory)
      return data
    },
    files: (parent, args, context, info) => {
      user.checkPermission(context, 'ADMIN')
      return context.db.query.files(args, info)
    }
  },
  Mutation: {
    uploadFile: async (parent, { comment, file }, context, info) => {
      user.checkPermission(context, 'ADMIN')
      const fileInfo = await uploadFile(file)

      return context.db.mutation.createFile(
        {
          data: {
            ...fileInfo,
            comment,
            user: { connect: { id: context.req.userId } }
          }
        },
        info
      )
    }
  }
}

async function fsFiles(directory: string) {
  const fileList = await fs.promises.readdir(directory)
  return Promise.all(
    fileList.map(async filename => {
      const path = `${directory}/${filename}`
      const { size, ctime, mtime } = await fs.promises.stat(path)
      return {
        filename,
        path,
        size,
        ctime,
        mtime
      }
    })
  )
}

async function uploadFile(file: any) {
  const { createReadStream, filename, mimetype, encoding } = await file
  const stream = createReadStream()

  const fsFilename = randombytes(16).toString('hex')
  const tmpPath = `${tmpDir}/${fsFilename}`
  const path = `${uploadDir}/${fsFilename}`
  const thumbnailPath = `${thumbnails}/${fsFilename}`
  await new Promise((resolve, reject) => {
    const file = fs.createWriteStream(tmpPath)
    file.on('finish', resolve)
    file.on('error', error => {
      fs.unlink(tmpPath, () => {
        reject(error)
      })
    })
    stream.on('error', (error: any) => file.destroy(error))
    stream.pipe(file)
  })

  if (mimetype.startsWith('image/')) {
    try {
      await processImage(tmpPath, path)
      await createThumbnail(tmpPath, thumbnailPath)
      await fs.promises.unlink(tmpPath)
    } catch (error) {
      try {
        await fs.promises.unlink(tmpPath)
        await fs.promises.unlink(path)
      } catch (ignore) {}
      throw error
    }
  }

  const { size } = await fs.promises.stat(path)

  return {
    filename,
    path,
    mimetype,
    encoding,
    size
  }
}

function processImage(tmpFile: string, outputFile: string) {
  return sharp(tmpFile)
    .resize(1600, 1600, {
      fit: sharp.fit.inside,
      withoutEnlargement: true
    })
    .jpeg()
    .toFile(outputFile)
}

function createThumbnail(tmpFile: string, thumbnail: string) {
  return sharp(tmpFile)
    .resize(200, 200, {
      fit: sharp.fit.inside
    })
    .jpeg()
    .toFile(thumbnail)
}
