"use strict";

const fs = require('fs');

const path = require('path');

const {
  Readable
} = require('stream');

const {
  removeKeysFromObject,
  convertObjectToStringGen
} = require('../utils/object-manipulation');

const pluginName = 'stats-plugin';
const statsSchema = {
  all: true
};

class StatsPlugin {
  constructor({
    statsOptions,
    statsOutputExcludeKeys,
    statsFileName
  }) {
    this.excludeKeysInStat = statsOutputExcludeKeys || [];
    this.statsFileName = statsFileName || 'bundle-report-integrity.json';
    this.statsOptions = Object.assign({}, statsSchema, statsOptions || {});
  }

  apply(compiler) {
    compiler.hooks.done.tapAsync(pluginName, (stats, callback) => {
      const statsJson = removeKeysFromObject(stats.toJson(this.statsOptions), this.excludeKeysInStat);
      this.emitStats(statsJson).on('end', () => {
        callback();
      }).on('error', e => {
        callback(e);
      });
    });
  }

  writeStatsFileInAStream(statsObj) {
    const {
      outputPath
    } = statsObj;
    const ouputFileName = path.join(outputPath, this.statsFileName);
    return this.createReadStream(statsObj).pipe(fs.createWriteStream(ouputFileName));
  }

  createReadStream(statsObj) {
    const excludeKeys = this.excludeKeysInStat;
    return new Readable({
      read() {
        let isDone = false;
        const objToStringGen = convertObjectToStringGen(statsObj, excludeKeys);

        while (!isDone) {
          const {
            done,
            value
          } = objToStringGen.next();

          if (done) {
            isDone = true;
            this.push(null);
          } else {
            this.push(value);
          }
        }
      }

    });
  }

  emitStats(statsJson) {
    return this.writeStatsFileInAStream(statsJson);
  }

}

module.exports = StatsPlugin;