// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

module.exports = function (grunt) {
  var _ = grunt.util._,
      fs = require('fs');

  grunt.registerMultiTask('get_deps', 'Fetch external dependencies', function () {

    grunt.log.writeln('Fetching external dependencies');
    const data = this.data,
          target = data.target || 'app/addons/',
          settingsFile = fs.existsSync(data.src) ? data.src : 'settings.json.default.json',
          settings = grunt.file.readJSON(settingsFile);

    const fetch = deps => {
      const fse = require('fs-extra');
      deps.forEach(dep => {
        const path = target + dep.name;
        const location = dep.url || dep.path;
        grunt.log.writeln('Fetching: ' + dep.name + ' (' + location + ')');
        try {
          fse.copySync(dep.path, path);
        } catch (e) {
          grunt.log.writeln('ERROR: ' + e.message);
        }
      });
    };

    const localDeps = settings.deps.filter(dep => !!dep.path);
    grunt.log.writeln(localDeps.length + ' local dependencies');
    fetch(localDeps);
  });

  grunt.registerMultiTask('gen_load_addons', 'Generate the load_addons.js file', function () {
    var data = this.data,
        _ = grunt.util._,
        settingsFile = fs.existsSync(data.src) ? data.src : 'settings.json.default.json',
        settings = grunt.file.readJSON(settingsFile),
        template = 'app/load_addons.js.underscore',
        dest = 'app/load_addons.js',
        deps = _.map(settings.deps, function (dep) {
          return './addons/' + dep.name + '/base';
        });

    var tmpl = _.template(grunt.file.read(template));
    grunt.file.write(dest, tmpl({deps: deps}));
  });

  grunt.registerMultiTask('gen_initialize', 'Generate app/initialize.js and assets/scss/_variables_overrides.sccs', function () {
    const _ = grunt.util._;
    const settings = this.data;
    const initTemplateFile = 'app/initialize.js.underscore';
    const initDestination = 'app/initialize.js';
    const initTemplate = _.template(grunt.file.read(initTemplateFile));
    const app = {};
    _.defaults(app, settings.app, {
      root: '/',
      host: '../..',
      version: "0.0"
    });
    grunt.file.write(initDestination, initTemplate(app));

    const overridesTemplateFile = 'assets/_variable_overrides.scss.underscore';
    const overridesDestination = 'assets/scss/_variable_overrides.scss';
    const overridesTemplate = _.template(grunt.file.read(overridesTemplateFile));
    const overridesVariables = {
      scss_overrides: settings.variables && settings.variables.scss_overrides,
      largeLogoPath: settings.variables && settings.variables.largeLogoPath,
      smallLogoPath: settings.variables && settings.variables.smallLogoPath
    };
    grunt.file.write(overridesDestination, overridesTemplate(overridesVariables));
  });

  // run every time nightwatch is executed from the command line
  grunt.registerMultiTask('initNightwatch', 'Sets up Nightwatch', function () {
    // perform a little validation on the settings
    _validateNightwatchSettings(this.data.settings);

    // figure out what tests we need to run by examining the settings.json file content. This method returns
    // the list of addon folders to test, plus a list of files to exclude
    var result = _getNightwatchTests(this.data.settings);
    var addonsWithTests = result.addonFolders;
    var excludeTests = result.excludeTests;
    var skipTags = result.skipTags;
    console.info('addons and excluded', addonsWithTests, excludeTests);
    console.info('skipTags', skipTags);

    // if the user passed a --file="X" on the command line, filter out
    var singleTestToRun = grunt.option('file');
    if (singleTestToRun) {
      addonsWithTests = _findSpecificNightwatchTest(addonsWithTests, singleTestToRun);
    }

    // now generate the new nightwatch.json file
    var nightwatchTemplate = _.template(grunt.file.read(this.data.template));
    grunt.file.write(this.data.dest, nightwatchTemplate({
      src_folders: JSON.stringify(addonsWithTests),
      exclude_tests: JSON.stringify(excludeTests, null, '\t'),
      custom_commands_path: JSON.stringify(this.data.settings.nightwatch.custom_commands_path),
      globals_path: this.data.settings.nightwatch.globals_path,
      fauxton_username: this.data.settings.nightwatch.fauxton_username,
      password: this.data.settings.nightwatch.password,
      launch_url: this.data.settings.nightwatch.launch_url,
      fauxton_host: _getHost(this.data.settings.nightwatch.fauxton_ip),
      fauxton_port: this.data.settings.nightwatch.fauxton_port,
      db_protocol: this.data.settings.nightwatch.db_protocol,
      db_host: this.data.settings.nightwatch.db_host,
      db_port: this.data.settings.nightwatch.db_port,
      selenium_port: this.data.settings.nightwatch.selenium_port,
      skiptags: skipTags
    }));
  });

  // HELPERS

  //if FAUXTON_HOST not set use ip address
  function _getHost (fauxton_ip) {
    if (fauxton_ip) {
      return fauxton_ip;
    }

    return "127.0.0.1";
  }

  function _validateNightwatchSettings (data) {
    var error = '';

    // if the settings file didn't contain any addons, it points to bigger problems!
    if (!data.deps.length) {
      error = 'No addons listed in settings.json - no tests to run!';

    // check the requires nightwatch settings. These should always exist in the settings.json file
    } else if (!_.has(data, 'nightwatch') ||
      !_.has(data.nightwatch, 'fauxton_username') ||
      !_.has(data.nightwatch, 'password')) {
      error = 'Your settings.json file doesn\'t contain valid nightwatch settings. Please check the user doc.';
    }

    if (error) {
      grunt.fail.fatal(error);
    }
  }

  function _findSpecificNightwatchTest (addonsWithTests, file) {
    var filename = file + '.js';

    var paths = addonsWithTests.reduce(function (acc, dir) {
      if (fs.existsSync(dir + '/' + filename)) {
        acc.push(dir + '/' + filename);
      }
      return acc;
    }, []);

    if (paths.length > 1) {
      grunt.fail.fatal('Found multiple nightwatch tests with that filename.');
    } else if (!paths.length) {
      grunt.fail.fatal('Found no testfile named ' + filename);
    }

    return paths[0];
  }

  function _getNightwatchTests (settings) {
    var testBlacklist = (_.has(settings.nightwatch, 'testBlacklist')) ? settings.nightwatch.testBlacklist : {};
    var addonFolders = [],
        excludeTests = [];

    _.each(settings.deps, function (addon) {
      var addonTestsFolder = 'app/addons/' + addon.name + '/tests/nightwatch';
      if (_.has(addon, 'path')) {
        addonTestsFolder = addon.path + '/tests/nightwatch';
      }

      // if this addon doesn't have any tests, just move along. Nothing to see here.
      if (!fs.existsSync(addonTestsFolder)) {
        return;
      }

      // next up: see if this addon has anything blacklisted
      if (_.has(testBlacklist, addon.name) && _.isArray(testBlacklist[addon.name]) && testBlacklist[addon.name].length > 0) {

        // a '*' means the user wants to blacklist all tests in the addon
        if (_.includes(testBlacklist[addon.name], '*')) {
          return;
        }

        // add the folder to test. Any specific files will be blacklisted separately
        addonFolders.push(addonTestsFolder);

        _.each(fs.readdirSync(addonTestsFolder), function (file) {
          if (_.includes(testBlacklist[addon.name], file)) {
            // the relative path is added to work around an oddity with nightwatch. It evaluates all exclude paths
            // relative to the current src_folder being examined, so we need to return to the root first
            excludeTests.push('../../../../../' + addonTestsFolder + '/' + file);
          }
        });

      } else {

        // add the whole folder
        addonFolders.push(addonTestsFolder);
      }
    });

    var skipTags = process.env.NIGHTWATCH_SKIPTAGS || 'nonpartitioned';
    var skipTagsJSON = JSON.stringify(skipTags.split(',').map(s => s.trim()));

    return {
      addonFolders: addonFolders,
      excludeTests: excludeTests,
      skipTags: skipTagsJSON
    };
  }

};
