'use strict';

const VersionChecker = require('ember-cli-version-checker');
const semver = require('semver');
const satisfies = semver.satisfies;
const gte = semver.gte;

function comparisonPlugin(babel) {
  const t = babel.types;

  const trueIdentifier = t.identifier('true');
  const falseIdentifier = t.identifier('false');

  return {
    name: "ember-compatibility-helpers",
    visitor: {
      ImportSpecifier(path, state) {
        if (path.parent.source.value === 'ember-compatibility-helpers') {
          let importedName = path.node.imported.name;
          if (importedName === 'gte') {
            state.gteImportId = state.gteImportId || path.scope.generateUidIdentifierBasedOnNode(path.node.id);
            path.scope.rename(path.node.local.name, state.gteImportId.name);
            path.remove();
          }

          if (importedName === 'lte') {
            state.lteImportId = state.lteImportId || path.scope.generateUidIdentifierBasedOnNode(path.node.id);
            path.scope.rename(path.node.local.name, state.lteImportId.name);
            path.remove();
          }
        }
      },

      CallExpression(path, state) {
        if (state.gteImportId && path.node.callee.name === state.gteImportId.name) {
          let argument = path.node.arguments[0];
          let replacementIdentifier = semver.gte(state.opts.emberVersion, argument.value) ? trueIdentifier : falseIdentifier;

          path.replaceWith(replacementIdentifier);
        } else if (state.lteImportId && path.node.callee.name === state.lteImportId.name) {
          let argument = path.node.arguments[0];
          let replacementIdentifier = semver.lte(state.opts.emberVersion, argument.value) ? trueIdentifier : falseIdentifier;

          path.replaceWith(replacementIdentifier);
        }
      }
    }
  };
}

comparisonPlugin.baseDir = () => __dirname;

module.exports = {
  name: 'ember-compatibility-helpers',

  included(appOrParentAddon) {
    this._super.included.apply(this, arguments);

    const host = this._findHost();

    // Create a root level version checker for checking the Ember version later on
    this.emberVersion = new VersionChecker(this).forEmber().version;

    // Create a parent checker for checking the parent app/addons dependencies (for things like polyfills)
    this.parentChecker = new VersionChecker(this.parent);
    const emberBabelChecker = this.parentChecker.for('ember-cli-babel', 'npm');

    if (!emberBabelChecker.satisfies('^6.0.0-beta.1')) {
      host.project.ui.writeWarnLine(
        'ember-compatibility-helpers: You are using an unsupported ember-cli-babel version, ' +
        'compatibility helper tranforms will not be included automatically'
      );

      this._registeredWithBabel = true;
    }

    this.registerTransformWithParent(appOrParentAddon);
  },

  /**
   * Registers the compatibility transforms with the parent addon or application
   *
   * @param {Addon|EmberAddon|EmberApp} parent
   */
  registerTransformWithParent(parent) {
    if (this._registeredWithBabel) return;

    const parentOptions = parent.options = parent.options || {};

    // Create babel options if they do not exist
    parentOptions.babel = parentOptions.babel || {};

    const plugins = parentOptions.babel.plugins = parentOptions.babel.plugins || [];
    const debugPlugin = this._getDebugPlugin(this.emberVersion, this.parentChecker);
    const comparisonPlugin = this._getComparisonPlugin(this.emberVersion);

    plugins.push(debugPlugin, comparisonPlugin);

    this._registeredWithBabel = true;
  },

  _getComparisonPlugin() {
    const trueEmberVersion = this.emberVersion.match(/\d+\.\d+\.\d+/)[0];

    return [comparisonPlugin, { emberVersion: trueEmberVersion }];
  },

  _getDebugPlugin(emberVersion, parentChecker) {
    const trueEmberVersion = emberVersion.match(/\d+\.\d+\.\d+/)[0];

    const DebugMacros = require('babel-plugin-debug-macros').default;

    let options = {
      envFlags: {
        source: 'ember-compatibility-helpers',
        flags: {
          DEBUG: false
        }
      },

      features: {
        name: 'ember-compatibility-helpers',
        source: 'ember-compatibility-helpers',
        flags: {
          HAS_UNDERSCORE_ACTIONS: !gte(trueEmberVersion, '2.0.0'),
          HAS_MODERN_FACTORY_INJECTIONS: gte(trueEmberVersion, '2.13.0'),
          HAS_DESCRIPTOR_TRAP: satisfies(trueEmberVersion, '~3.0.0'),
          HAS_NATIVE_COMPUTED_GETTERS: gte(trueEmberVersion, '3.1.0-beta.1'),

          GTE_EMBER_1_13: gte(trueEmberVersion, '1.13.0'),
          IS_EMBER_2: gte(trueEmberVersion, '2.0.0'),
          IS_GLIMMER_2: gte(trueEmberVersion, '2.10.0'),

          SUPPORTS_FACTORY_FOR: gte(trueEmberVersion, '2.12.0') || parentChecker.for('ember-factory-for-polyfill', 'npm').gte('1.0.0'),
          SUPPORTS_GET_OWNER: gte(trueEmberVersion, '2.3.0') || parentChecker.for('ember-getowner-polyfill', 'npm').gte('1.1.0'),
          SUPPORTS_SET_OWNER: gte(trueEmberVersion, '2.3.0'),
          SUPPORTS_NEW_COMPUTED: gte(trueEmberVersion, '1.12.0-beta.1'),
          SUPPORTS_INVERSE_BLOCK: gte(trueEmberVersion, '1.13.0'),
          SUPPORTS_CLOSURE_ACTIONS: gte(trueEmberVersion, '1.13.0'),
          SUPPORTS_UNIQ_BY_COMPUTED: gte(trueEmberVersion, '2.7.0')
        }
      },

      externalizeHelpers: {
        global: 'Ember'
      },

      debugTools: {
        source: 'ember-compatibility-helpers',
        assertPredicateIndex: 1
      }
    };

    return [DebugMacros, options];
  },

  _findHost() {
    let current = this;
    let app;

    do {
      app = current.app || app;
    } while (current.parent.parent && (current = current.parent));

    return app;
  }
};