'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _regenerator;

function _load_regenerator() {
  return _regenerator = _interopRequireDefault(require('babel-runtime/regenerator'));
}

var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

var _path;

function _load_path() {
  return _path = _interopRequireDefault(require('path'));
}

var _ramda;

function _load_ramda() {
  return _ramda = _interopRequireDefault(require('ramda'));
}

var _fsExtra;

function _load_fsExtra() {
  return _fsExtra = _interopRequireDefault(require('fs-extra'));
}

var _semver;

function _load_semver() {
  return _semver = _interopRequireDefault(require('semver'));
}

var _logger;

function _load_logger() {
  return _logger = _interopRequireDefault(require('../logger/logger'));
}

var _logger2;

function _load_logger2() {
  return _logger2 = require('../logger/logger');
}

var _scope;

function _load_scope() {
  return _scope = require('../scope');
}

var _exceptions;

function _load_exceptions() {
  return _exceptions = require('../scope/exceptions');
}

var _bitId;

function _load_bitId() {
  return _bitId = require('../bit-id');
}

var _extensionNameNotValid;

function _load_extensionNameNotValid() {
  return _extensionNameNotValid = _interopRequireDefault(require('./exceptions/extension-name-not-valid'));
}

var _analytics;

function _load_analytics() {
  return _analytics = require('../analytics/analytics');
}

var _extensionLoadError;

function _load_extensionLoadError() {
  return _extensionLoadError = _interopRequireDefault(require('./exceptions/extension-load-error'));
}

var _environment;

function _load_environment() {
  return _environment = _interopRequireDefault(require('../environment'));
}

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var CORE_EXTENSIONS_PATH = './core-extensions';

// export type BaseExtensionProps = {
//   ...InstanceSpecificProps,
//   ...StaticProps
// };

// type staticProps = $Diff<BaseExtensionProps, instanceSpecificProps>

var BaseExtension = function () {
  function BaseExtension(extensionProps) {
    _classCallCheck(this, BaseExtension);

    this.api = _getConcreteBaseAPI({ name: this.name });

    this.name = extensionProps.name;
    this.rawConfig = extensionProps.rawConfig;
    this.options = extensionProps.options;
    this.dynamicConfig = extensionProps.dynamicConfig || extensionProps.rawConfig;
    this.context = extensionProps.context;
    this.script = extensionProps.script;
    this.disabled = extensionProps.disabled;
    this.filePath = extensionProps.filePath;
    this.rootDir = extensionProps.rootDir || '';
    this.loaded = extensionProps.loaded;
    this.api = extensionProps.api;
  }

  /**
   * Run the extension's init function
   */
  // Store the required plugin


  _createClass(BaseExtension, [{
    key: 'init',
    value: function () {
      var _ref = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee() {
        var throws = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
        return (_regenerator || _load_regenerator()).default.wrap(function _callee$(_context) {
          while (1) {
            switch (_context.prev = _context.next) {
              case 0:
                (_analytics || _load_analytics()).Analytics.addBreadCrumb('base-extension', 'initialize extension');
                _context.prev = 1;

                if (!(this.script && this.script.init && typeof this.script.init === 'function')) {
                  _context.next = 5;
                  break;
                }

                _context.next = 5;
                return this.script.init({ rawConfig: this.rawConfig, dynamicConfig: this.dynamicConfig, api: this.api });

              case 5:
                this.initialized = true;
                // Make sure to not kill the process if an extension didn't load correctly
                _context.next = 16;
                break;

              case 8:
                _context.prev = 8;
                _context.t0 = _context['catch'](1);

                (_logger || _load_logger()).default.error(`initialized extension ${this.name} failed`);
                (_logger || _load_logger()).default.error(_context.t0);

                if (!throws) {
                  _context.next = 14;
                  break;
                }

                throw new (_extensionLoadError || _load_extensionLoadError()).default(_context.t0, this.name);

              case 14:
                this.initialized = false;
                return _context.abrupt('return', false);

              case 16:
                return _context.abrupt('return', true);

              case 17:
              case 'end':
                return _context.stop();
            }
          }
        }, _callee, this, [[1, 8]]);
      }));

      function init() {
        return _ref.apply(this, arguments);
      }

      return init;
    }()
  }, {
    key: 'extendAPI',
    value: function extendAPI(baseApi, api) {
      this.api = (_ramda || _load_ramda()).default.merge(baseApi, api);
    }
  }, {
    key: 'toString',
    value: function toString() {
      return JSON.stringify(this, null, 2);
    }
  }, {
    key: 'toBitJsonObject',
    value: function toBitJsonObject() {
      return {
        [this.name]: {
          rawConfig: this.rawConfig,
          options: this.options
        }
      };
    }
  }, {
    key: 'toModelObject',
    value: function toModelObject() {
      return {
        name: this.name,
        config: this.dynamicConfig
      };
    }
  }, {
    key: 'toObject',
    value: function toObject() {
      return this.toModelObject();
    }

    /**
     * Reload the extension, this mainly contain the process of going to the extension file requiring it and get the dynamic config
     * It mostly used for env extension when sometime on the first load the env didn't installed yet (only during build / test) phase
     */

  }, {
    key: 'reload',
    value: function () {
      var _ref3 = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee2(scopePath, _ref2) {
        var throws = _ref2.throws;

        var _getExtensionPath2, _resolvedPath, _componentPath, baseProps;

        return (_regenerator || _load_regenerator()).default.wrap(function _callee2$(_context2) {
          while (1) {
            switch (_context2.prev = _context2.next) {
              case 0:
                (_analytics || _load_analytics()).Analytics.addBreadCrumb('base-extension', 'reload extension');
                if (!this.filePath && !this.options.core) {
                  _getExtensionPath2 = _getExtensionPath(this.name, scopePath, this.options.core), _resolvedPath = _getExtensionPath2.resolvedPath, _componentPath = _getExtensionPath2.componentPath;

                  this.filePath = _resolvedPath;
                  this.rootDir = _componentPath;
                }
                this.name = _addVersionToNameFromPathIfMissing(this.name, this.rootDir);
                _context2.next = 5;
                return BaseExtension.loadFromFile({
                  name: this.name,
                  filePath: this.filePath,
                  rootDir: this.rootDir,
                  rawConfig: this.rawConfig,
                  options: this.options,
                  throws
                });

              case 5:
                baseProps = _context2.sent;

                if (baseProps.loaded) {
                  this.loaded = baseProps.loaded;
                  this.script = baseProps.script;
                  this.dynamicConfig = baseProps.dynamicConfig;
                }

              case 7:
              case 'end':
                return _context2.stop();
            }
          }
        }, _callee2, this);
      }));

      function reload(_x2, _x3) {
        return _ref3.apply(this, arguments);
      }

      return reload;
    }()
  }, {
    key: 'setExtensionPathInScope',
    value: function setExtensionPathInScope(scopePath) {
      var _getExtensionPath3 = _getExtensionPath(this.name, scopePath, this.options.core),
          resolvedPath = _getExtensionPath3.resolvedPath,
          componentPath = _getExtensionPath3.componentPath;

      this.filePath = resolvedPath;
      this.rootDir = componentPath;
    }
  }], [{
    key: 'transformStringToModelObject',
    value: function transformStringToModelObject(name) {
      return {
        name,
        config: {}
      };
    }

    /**
     * Load extension by name
     * The extension will be from scope by default or from file
     * if there is file(path) in the options
     * The file path is relative to the bit.json of the project or absolute
     * @param {string} name - name of the extension
     * @param {Object} rawConfig - raw config for the extension
     * @param {Object} options - extension options such as - disabled, file, core
     * @param {string} consumerPath - path to the consumer folder (to load the file relatively)
     * @param {string} scopePath - scope which stores the extension code
     */

  }, {
    key: 'load',
    value: function () {
      var _ref5 = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee3(_ref4) {
        var name = _ref4.name,
            _ref4$rawConfig = _ref4.rawConfig,
            rawConfig = _ref4$rawConfig === undefined ? {} : _ref4$rawConfig,
            _ref4$options = _ref4.options,
            options = _ref4$options === undefined ? {} : _ref4$options,
            consumerPath = _ref4.consumerPath,
            scopePath = _ref4.scopePath,
            _ref4$throws = _ref4.throws,
            throws = _ref4$throws === undefined ? false : _ref4$throws,
            context = _ref4.context;

        var concreteBaseAPI, absPath, _file, _staticExtensionProps, _extensionProps, staticExtensionProps, _getExtensionPath4, _resolvedPath2, _componentPath2, nameWithVersion, extensionProps;

        return (_regenerator || _load_regenerator()).default.wrap(function _callee3$(_context3) {
          while (1) {
            switch (_context3.prev = _context3.next) {
              case 0:
                (_analytics || _load_analytics()).Analytics.addBreadCrumb('base-extension', 'load extension');
                concreteBaseAPI = _getConcreteBaseAPI({ name });

                if (!options.file) {
                  _context3.next = 12;
                  break;
                }

                absPath = options.file;
                _file = options.file || '';

                if (!(_path || _load_path()).default.isAbsolute(options.file) && consumerPath) {
                  absPath = (_path || _load_path()).default.resolve(consumerPath, _file);
                }
                _context3.next = 8;
                return BaseExtension.loadFromFile({
                  name,
                  filePath: absPath,
                  rawConfig,
                  options,
                  throws
                });

              case 8:
                _staticExtensionProps = _context3.sent;
                _extensionProps = _extends({ api: concreteBaseAPI, context }, _staticExtensionProps);

                _extensionProps.api = concreteBaseAPI;
                return _context3.abrupt('return', _extensionProps);

              case 12:
                staticExtensionProps = {
                  name,
                  rawConfig,
                  dynamicConfig: rawConfig,
                  options,
                  disabled: false,
                  loaded: false,
                  filePath: ''
                };
                // Require extension from scope

                if (!scopePath) {
                  _context3.next = 19;
                  break;
                }

                // $FlowFixMe
                _getExtensionPath4 = _getExtensionPath(name, scopePath, options.core), _resolvedPath2 = _getExtensionPath4.resolvedPath, _componentPath2 = _getExtensionPath4.componentPath;
                nameWithVersion = _addVersionToNameFromPathIfMissing(name, _componentPath2);
                _context3.next = 18;
                return BaseExtension.loadFromFile({
                  name: nameWithVersion,
                  filePath: _resolvedPath2,
                  rootDir: _componentPath2,
                  rawConfig,
                  options,
                  throws
                });

              case 18:
                staticExtensionProps = _context3.sent;

              case 19:
                extensionProps = _extends({ api: concreteBaseAPI, context }, staticExtensionProps);
                return _context3.abrupt('return', extensionProps);

              case 21:
              case 'end':
                return _context3.stop();
            }
          }
        }, _callee3, this);
      }));

      function load(_x4) {
        return _ref5.apply(this, arguments);
      }

      return load;
    }()
  }, {
    key: 'loadFromModelObject',
    value: function loadFromModelObject(modelObject) {
      (_analytics || _load_analytics()).Analytics.addBreadCrumb('base-extension', 'load extension from model object');
      var staticExtensionProps = void 0;
      if (typeof modelObject === 'string') {
        staticExtensionProps = {
          name: modelObject,
          rawConfig: {},
          dynamicConfig: {},
          options: {},
          disabled: false,
          loaded: false,
          filePath: ''
        };
      } else {
        staticExtensionProps = {
          name: modelObject.name,
          rawConfig: modelObject.config,
          dynamicConfig: modelObject.config,
          options: {},
          disabled: false,
          loaded: false,
          filePath: ''
        };
      }

      var concreteBaseAPI = _getConcreteBaseAPI({ name: staticExtensionProps.name });
      var extensionProps = _extends({ api: concreteBaseAPI }, staticExtensionProps);
      return extensionProps;
    }
  }, {
    key: 'loadFromFile',
    value: function () {
      var _ref7 = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee4(_ref6) {
        var name = _ref6.name,
            filePath = _ref6.filePath,
            rootDir = _ref6.rootDir,
            _ref6$rawConfig = _ref6.rawConfig,
            rawConfig = _ref6$rawConfig === undefined ? {} : _ref6$rawConfig,
            _ref6$options = _ref6.options,
            options = _ref6$options === undefined ? {} : _ref6$options,
            _ref6$throws = _ref6.throws,
            throws = _ref6$throws === undefined ? false : _ref6$throws;

        var extensionProps, isFileExist, err, _script, msg;

        return (_regenerator || _load_regenerator()).default.wrap(function _callee4$(_context4) {
          while (1) {
            switch (_context4.prev = _context4.next) {
              case 0:
                (_logger || _load_logger()).default.info(`loading extension ${name} from ${filePath}`);
                (_analytics || _load_analytics()).Analytics.addBreadCrumb('base-extension', 'load extension from file');
                extensionProps = {
                  name,
                  rawConfig,
                  dynamicConfig: rawConfig,
                  options,
                  disabled: false,
                  loaded: false,
                  filePath: '',
                  rootDir: ''
                };
                // Skip disabled extensions

                if (!options.disabled) {
                  _context4.next = 8;
                  break;
                }

                extensionProps.disabled = true;
                (_logger || _load_logger()).default.info(`skip extension ${extensionProps.name} because it is disabled`);
                extensionProps.loaded = false;
                return _context4.abrupt('return', extensionProps);

              case 8:
                extensionProps.filePath = filePath;
                extensionProps.rootDir = rootDir;
                _context4.next = 12;
                return (_fsExtra || _load_fsExtra()).default.exists(filePath);

              case 12:
                isFileExist = _context4.sent;

                if (isFileExist) {
                  _context4.next = 19;
                  break;
                }

                if (!(throws && options.file)) {
                  _context4.next = 17;
                  break;
                }

                err = new Error(`the file ${filePath} not found`);
                throw new (_extensionLoadError || _load_extensionLoadError()).default(err, extensionProps.name);

              case 17:
                extensionProps.loaded = false;
                return _context4.abrupt('return', extensionProps);

              case 19:
                if (!(rootDir && !(_environment || _load_environment()).default.isEnvironmentInstalled(rootDir))) {
                  _context4.next = 22;
                  break;
                }

                extensionProps.loaded = false;
                return _context4.abrupt('return', extensionProps);

              case 22:
                _context4.prev = 22;

                // $FlowFixMe
                _script = require(filePath); // eslint-disable-line

                extensionProps.script = _script.default ? _script.default : _script;

                if (!(extensionProps.script.getDynamicConfig && typeof extensionProps.script.getDynamicConfig === 'function')) {
                  _context4.next = 29;
                  break;
                }

                _context4.next = 28;
                return extensionProps.script.getDynamicConfig({ rawConfig });

              case 28:
                extensionProps.dynamicConfig = _context4.sent;

              case 29:
                _context4.next = 40;
                break;

              case 31:
                _context4.prev = 31;
                _context4.t0 = _context4['catch'](22);

                if (_context4.t0.code === 'MODULE_NOT_FOUND') {
                  msg = `loading extension ${extensionProps.name} failed, the file ${extensionProps.filePath} not found`;

                  (_logger || _load_logger()).default.warn(msg);
                  // console.error(msg); // eslint-disable-line no-console
                }
                (_logger || _load_logger()).default.error(`loading extension ${extensionProps.name} failed`);
                (_logger || _load_logger()).default.error(_context4.t0);
                extensionProps.loaded = false;

                if (!throws) {
                  _context4.next = 39;
                  break;
                }

                throw new (_extensionLoadError || _load_extensionLoadError()).default(_context4.t0, extensionProps.name);

              case 39:
                return _context4.abrupt('return', extensionProps);

              case 40:
                extensionProps.loaded = true;
                return _context4.abrupt('return', extensionProps);

              case 42:
              case 'end':
                return _context4.stop();
            }
          }
        }, _callee4, this, [[22, 31]]);
      }));

      function loadFromFile(_x5) {
        return _ref7.apply(this, arguments);
      }

      return loadFromFile;
    }()
  }]);

  return BaseExtension;
}();

exports.default = BaseExtension;


var _getExtensionPath = function _getExtensionPath(name, scopePath) {
  var isCore = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;

  if (isCore) {
    return _getCoreExtensionPath(name);
  }
  if (!scopePath) {
    throw new (_exceptions || _load_exceptions()).ScopeNotFound();
  }
  return _getRegularExtensionPath(name, scopePath);
};

var _getCoreExtensionPath = function _getCoreExtensionPath(name) {
  var componentPath = (_path || _load_path()).default.join(__dirname, CORE_EXTENSIONS_PATH, name);
  return {
    resolvedPath: componentPath,
    componentPath
  };
};

var _getRegularExtensionPath = function _getRegularExtensionPath(name, scopePath) {
  var bitId = void 0;
  try {
    bitId = (_bitId || _load_bitId()).BitId.parse(name);
  } catch (err) {
    throw new (_extensionNameNotValid || _load_extensionNameNotValid()).default(name);
  }
  if (!bitId || !bitId.scope) throw new (_extensionNameNotValid || _load_extensionNameNotValid()).default(name);

  var internalComponentsPath = (_scope || _load_scope()).Scope.getComponentsRelativePath();
  var internalComponentPath = (_scope || _load_scope()).Scope.getComponentRelativePath(bitId, scopePath);
  var componentPath = (_path || _load_path()).default.join(scopePath, internalComponentsPath, internalComponentPath);
  try {
    // This might throw an error in case of imported component when the env
    // isn't installed yet
    // It will be handled in higher functions
    var resolved = require.resolve(componentPath);
    return {
      resolvedPath: resolved,
      componentPath
    };
  } catch (e) {
    return {
      resolvedPath: componentPath,
      componentPath
    };
  }
};

var _getExtensionVersionFromComponentPath = function _getExtensionVersionFromComponentPath(componentPath) {
  var parsed = (_path || _load_path()).default.parse(componentPath);
  var version = parsed.base;
  if (!(_semver || _load_semver()).default.valid(version)) {
    return undefined;
  }
  return version;
};

var _addVersionToNameFromPathIfMissing = function _addVersionToNameFromPathIfMissing(name, componentPath) {
  var bitId = void 0;
  try {
    bitId = (_bitId || _load_bitId()).BitId.parse(name);
  } catch (err) {
    throw new (_extensionNameNotValid || _load_extensionNameNotValid()).default(name);
  }
  if (bitId.getVersion().latest) {
    var version = _getExtensionVersionFromComponentPath(componentPath);
    bitId.version = version;
    return bitId.toString();
  }
  return name;
};

var baseApi = {
  /**
   * API to get logger
   */
  getLogger: function getLogger(name) {
    return function () {
      return (0, (_logger2 || _load_logger2()).createExtensionLogger)(name);
    };
  }
};

/**
 * Function which get actual params and return a concrete base api
 */
var _getConcreteBaseAPI = function _getConcreteBaseAPI(_ref8) {
  var name = _ref8.name;

  var concreteBaseAPI = (_ramda || _load_ramda()).default.clone(baseApi);
  concreteBaseAPI.getLogger = baseApi.getLogger(name);
  return concreteBaseAPI;
};