/**
 * Module dependencies.
 */

const Concat = require('concat-with-sourcemaps');
const through = require('through2');
const lodashTemplate = require('lodash.template');
const stream = require('stream');
const path = require('path');

/**
 * gulp-header plugin
 */

module.exports = (headerText, data) => {
  headerText = headerText || '';

  function TransformStream(file, enc, cb) {
    // format template
    const filename = path.basename(file.path);
    const template =
      data === false ?
        headerText
        : lodashTemplate(headerText)(
            Object.assign({}, file.data || {}, { file: file, filename: filename }, data)
          );

    if (file && typeof file === 'string') {
      this.push(template + file);
      return cb();
    }

    // if not an existing file, passthrough
    if (!isExistingFile(file)) {
      this.push(file);
      return cb();
    }

    // handle file stream;
    if (file.isStream()) {
      const stream = through();
      stream.write(Buffer.from(template));
      stream.on('error', this.emit.bind(this, 'error'));
      file.contents = file.contents.pipe(stream);
      this.push(file);
      return cb();
    }

    // variables to handle direct file content manipulation
    const concat = new Concat(true, filename);

    // add template
    concat.add(null, Buffer.from(template));

    // add sourcemap
    concat.add(file.relative, file.contents, file.sourceMap);

    // make sure streaming content is preserved
    if (file.contents && !isStream(file.contents)) {
      file.contents = concat.content;
    }

    // apply source map
    if (concat.sourceMapping) {
      file.sourceMap = JSON.parse(concat.sourceMap);
    }

    // make sure the file goes through the next gulp plugin
    this.push(file);

    // tell the stream engine that we are done with this file
    cb();
  }

  return through.obj(TransformStream);
};

/**
 * is stream?
 */
const isStream = (obj) => {
  return obj instanceof stream.Stream;
};

/**
 * Is File, and Exists
 */
const isExistingFile = (file) => {
  try {
    if (!(file && typeof file === 'object')) return false;
    if (file.isDirectory()) return false;
    if (file.isStream()) return true;
    if (file.isBuffer()) return true;
    if (typeof file.contents === 'string') return true;
  } catch (err) {}
  return false;
};
