const formidable = require('formidable');
const fs = require('fs');
const config = require('../config');
const path = require('path');
const { format } = require('util');

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 (req, res) => {
    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;
      }
    }

    const form = new formidable.IncomingForm();
    form.uploadDir = config.uploadDir;

    if (validations && validations.maxFileSize) {
      form.maxFileSize = validations.maxFileSize;
    }

    form.parse(req, function (err, fields, files) {
      const filename = String(fields.filename);
      const fileTempUrl = files.file.path;

      if (!filename) {
        fs.unlinkSync(fileTempUrl);
        res.sendStatus(500);
        return;
      }

      const privateUrl = path.join(form.uploadDir, folder, filename);
      ensureDirectoryExistence(privateUrl);
      fs.renameSync(fileTempUrl, privateUrl);
      res.sendStatus(200);
    });

    form.on('error', function (err) {
      res.status(500).send(err);
    });
  };
};

const downloadLocal = async (req, res) => {
  const privateUrl = req.query.privateUrl;
  if (!privateUrl) {
    return res.sendStatus(404);
  }
  res.download(path.join(config.uploadDir, privateUrl));
};

const initGCloud = () => {
  const processFile = require('../middlewares/upload');
  const { Storage } = require('@google-cloud/storage');

  const crypto = require('crypto');
  const hash = config.gcloud.hash;

  const privateKey = process.env.GC_PRIVATE_KEY.replace(/\\\n/g, '\n');

  const storage = new Storage({
    projectId: process.env.GC_PROJECT_ID,
    credentials: {
      client_email: process.env.GC_CLIENT_EMAIL,
      private_key: privateKey,
    },
  });

  const bucket = storage.bucket(config.gcloud.bucket);
  return { hash, bucket, processFile };
};

const uploadGCloud = async (folder, req, res) => {
  try {
    const { hash, bucket, processFile } = initGCloud();
    await processFile(req, res);
    let buffer = await req.file.buffer;
    let filename = await req.body.filename;

    if (!req.file) {
      return res.status(400).send({ message: 'Please upload a file!' });
    }

    let path = `${hash}/${folder}/${filename}`;
    let blob = bucket.file(path);

    console.log(path);

    const blobStream = blob.createWriteStream({
      resumable: false,
    });

    blobStream.on('error', (err) => {
      console.log('Upload error');
      console.log(err.message);
      res.status(500).send({ message: err.message });
    });

    console.log(`https://storage.googleapis.com/${bucket.name}/${blob.name}`);

    blobStream.on('finish', async (data) => {
      const publicUrl = format(
        `https://storage.googleapis.com/${bucket.name}/${blob.name}`,
      );

      res.status(200).send({
        message: 'Uploaded the file successfully: ' + path,
        url: publicUrl,
      });
    });

    blobStream.end(buffer);
  } catch (err) {
    console.log(err);

    res.status(500).send({
      message: `Could not upload the file. ${err}`,
    });
  }
};

const downloadGCloud = async (req, res) => {
  try {
    const { hash, bucket, processFile } = initGCloud();

    const privateUrl = await req.query.privateUrl;
    const filePath = `${hash}/${privateUrl}`;
    const file = bucket.file(filePath);
    const fileExists = await file.exists();

    if (fileExists[0]) {
      const stream = file.createReadStream();
      stream.pipe(res);
    } else {
      res.status(404).send({
        message: 'Could not download the file. ' + err,
      });
    }
  } catch (err) {
    res.status(404).send({
      message: 'Could not download the file. ' + err,
    });
  }
};

const deleteGCloud = async (privateUrl) => {
  try {
    const { hash, bucket, processFile } = initGCloud();
    const filePath = `${hash}/${privateUrl}`;

    const file = bucket.file(filePath);
    const fileExists = await file.exists();

    if (fileExists[0]) {
      file.delete();
    }
  } catch (err) {
    console.log(`Cannot find the file ${privateUrl}`);
  }
};

module.exports = {
  initGCloud,
  uploadLocal,
  downloadLocal,
  deleteGCloud,
  uploadGCloud,
  downloadGCloud,
};
