const fs = require('fs');
const fsPromises = require('fs/promises');
const config = require('../config');
const path = require('path');
const processFile = require('../middlewares/upload');
const { scanBuffer } = require('../security/malwareScan');
const { validateFileUpload } = require('../security/fileUpload');

const safeUploadPath = (privateUrl) => {
  if (typeof privateUrl !== 'string' || privateUrl.includes('\0')) {
    return null;
  }

  const uploadRoot = path.resolve(config.uploadDir);
  const resolvedPath = path.resolve(uploadRoot, privateUrl);
  if (resolvedPath !== uploadRoot && !resolvedPath.startsWith(`${uploadRoot}${path.sep}`)) {
    return null;
  }

  return resolvedPath;
};

const ensureDirectoryExistence = (filePath) => {
  const dirname = path.dirname(filePath);

  if (fs.existsSync(dirname)) {
    return true;
  }

  ensureDirectoryExistence(dirname);
  fs.mkdirSync(dirname);
}

const uploadLocal = (
  folder,
  validations = {
    entity: null,
    maxFileSize: null,
    folderIncludesAuthenticationUid: false,
  },
) => {
  return async (req, res, next) => {
    if (!req.currentUser) {
      res.sendStatus(403);
      return;
    }

    if (
      validations.entity
    ) {
      res.sendStatus(403);
      return;
    }

    if (validations.folderIncludesAuthenticationUid) {
      folder = folder.replace(
        ':userId',
        req.currentUser.authenticationUid,
      );
      if (
        !req.currentUser.authenticationUid ||
        !folder.includes(req.currentUser.authenticationUid)
      ) {
        res.sendStatus(403);
        return;
      }
    }

    try {
      await processFile(req, res);
      if (!req.file) {
        res.status(400).send({ error: 'file_missing' });
        return;
      }

      const filename = path.basename(String(req.body.filename || req.file.originalname || ''));
      const privateUrl = safeUploadPath(path.join(folder, filename));
      if (!filename || !privateUrl) {
        res.status(400).send({ error: 'file_path_invalid' });
        return;
      }

      validateFileUpload(req.file, filename);
      await scanBuffer(req.file.buffer);
      ensureDirectoryExistence(privateUrl);
      await fsPromises.writeFile(privateUrl, req.file.buffer, { mode: 0o600 });
      res.sendStatus(200);
    } catch (error) {
      next(error);
    }
  }
}

const downloadLocal = (req, res) => {
    const privateUrl = req.query.privateUrl;
    if (!privateUrl) {
      return res.sendStatus(404);
    }
    const filePath = safeUploadPath(privateUrl);
    if (!filePath) {
      return res.status(400).send({ error: 'file_path_invalid' });
    }
    res.download(filePath);
}

module.exports = {
  uploadLocal,
  downloadLocal,
}
