'use strict';

const fs = require('fs-extra');
const path = require('path');
const readdir = require('recursive-readdir');
// Documentation of @google-cloud/storage: https://cloud.google.com/nodejs/docs/reference/storage/2.3.x/Bucket#upload
const { Storage } = require('@google-cloud/storage');

const log = require('intel').getLogger('sitespeedio.plugin.gcs');
const throwIfMissing = require('../../support/util').throwIfMissing;

async function upload(dir, gcsOptions, prefix) {
  const files = await readdir(dir);
  const promises = [];

  const storage = new Storage({
    projectId: gcsOptions.projectId,
    keyFilename: gcsOptions.key
  });

  const bucket = storage.bucket(gcsOptions.bucketname);

  for (let file of files) {
    const stats = fs.statSync(file);

    if (stats.isFile()) {
      promises.push(uploadFile(file, bucket, gcsOptions, prefix, dir));
    } else {
      log.debug(`Will not upload ${file} since it is not a file`);
    }
  }
  return Promise.all(promises);
}

async function uploadFile(file, bucket, gcsOptions, prefix, baseDir) {
  const subPath = path.relative(baseDir, file);
  const fileName = path.join(gcsOptions.path || prefix, subPath);

  const params = {
    public: !!gcsOptions.public,
    destination: fileName,
    resumable: false,
    validation: 'crc32c',
    gzip: !!gcsOptions.gzip,
    metadata: {
      metadata: {
        cacheControl: 'public, max-age=31536000'
      }
    }
  };

  return bucket.upload(file, params);
}

module.exports = {
  open(context, options) {
    this.gcsOptions = options.gcs;
    this.make = context.messageMaker('gcs').make;
    throwIfMissing(this.gcsOptions, ['bucketname'], 'gcs');
    this.storageManager = context.storageManager;
  },

  async processMessage(message, queue) {
    if (message.type === 'sitespeedio.setup') {
      // Let other plugins know that the GCS plugin is alive
      queue.postMessage(this.make('gcs.setup'));
    } else if (message.type === 'html.finished') {
      const make = this.make;
      const gcsOptions = this.gcsOptions;
      const baseDir = this.storageManager.getBaseDir();

      log.info(
        `Uploading ${baseDir} to Google Cloud Storage bucket ${
          gcsOptions.bucketname
        }, this can take a while ...`
      );

      try {
        await upload(
          baseDir,
          gcsOptions,
          this.storageManager.getStoragePrefix()
        );
        log.info('Finished upload to Google Cloud Storage');
        if (gcsOptions.public) {
          log.info(
            'Uploaded results on Google Cloud storage are publicly readable'
          );
        }
        if (gcsOptions.removeLocalResult) {
          await fs.remove(baseDir);
          log.debug(`Removed local files and directory ${baseDir}`);
        } else {
          log.debug(
            `Local result files and directories are stored in ${baseDir}`
          );
        }
      } catch (e) {
        queue.postMessage(make('error', e));
        log.error('Could not upload to Google Cloud Storage', e);
      }
      queue.postMessage(make('gcs.finished'));
    }
  }
};

@@ -425,16 +425,531 @@
 ssing;%0A%0A
+async function uploadLatestFiles(dir, gcsOptions, prefix) %7B%0A  function ignoreDirs(file, stats) %7B%0A    return stats.isDirectory();%0A  %7D%0A%0A  const storage = new Storage(%7B%0A    projectId: gcsOptions.projectId,%0A    keyFilename: gcsOptions.key%0A  %7D);%0A  const bucket = storage.bucket(gcsOptions.bucketname);%0A%0A  const files = await readdir(dir, %5BignoreDirs%5D);%0A  const promises = %5B%5D;%0A%0A  for (let file of files) %7B%0A    promises.push(uploadFile(file, bucket, gcsOptions, prefix, dir, true));%0A  %7D%0A  return Promise.all(promises);%0A%7D%0A%0A
 async fu
@@ -985,24 +985,24 @@
 , prefix) %7B%0A
-
   const file
@@ -1525,37 +1525,42 @@
  uploadFile(
+%0A  
 file,
+%0A 
  bucket,
  gcsOptions,
@@ -1539,32 +1539,34 @@
  file,%0A  bucket,
+%0A 
  gcsOptions, pre
@@ -1561,24 +1561,26 @@
 Options,
+%0A 
  prefix,
  baseDir
@@ -1571,24 +1571,26 @@
  prefix,
+%0A 
  baseDir
 ) %7B%0A  co
@@ -1581,16 +1581,32 @@
  baseDir
+,%0A  noCacheTime%0A
 ) %7B%0A  co
@@ -1949,16 +1949,38 @@
 age=
+' + noCacheTime ? 0 : 
 31536000
 '%0A  
@@ -1975,17 +1975,16 @@
 31536000
-'
 %0A      %7D
@@ -2118,16 +2118,44 @@
 ns.gcs;%0A
+    this.options = options;%0A
     this
@@ -2975,16 +2975,16 @@
 refix()%0A
-
         
@@ -2978,32 +2978,324 @@
 ix()%0A        );%0A
+        if (this.options.copyLatestFilesToBase) %7B%0A          const rootPath = path.resolve(baseDir, '..');%0A          const dirsAsArray = rootPath.split(path.sep);%0A          const rootName = dirsAsArray.slice(-1)%5B0%5D;%0A          await uploadLatestFiles(rootPath, gcsOptions, rootName);%0A        %7D%0A
         log.info

Support upload latest file for GCS. (#3258)