'use strict';

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

var _pathExtra = require('path-extra');

var _semver = require('semver');

var _semver2 = _interopRequireDefault(_semver);

var _events = require('events');

var _events2 = _interopRequireDefault(_events);

var _fsExtra = require('fs-extra');

var _glob = require('glob');

var _glob2 = _interopRequireDefault(_glob);

var _lodash = require('lodash');

var _lodash2 = _interopRequireDefault(_lodash);

var _electron = require('electron');

var _nodeFetch = require('node-fetch');

var _nodeFetch2 = _interopRequireDefault(_nodeFetch);

var _pluginManagerUtils = require('./plugin-manager-utils');

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"); }); }; }

const __ = window.i18n.setting.__.bind(window.i18n.setting);
const { config, toast, proxy, ROOT, PLUGIN_PATH, dispatch, getStore } = window;

const fetchHeader = new Headers();
fetchHeader.set("Cache-Control", "max-age=0");
const defaultFetchOption = {
  method: "GET",
  cache: "default",
  headers: fetchHeader
};

function defaultPluginPath(packageName) {
  return (0, _pathExtra.join)(PLUGIN_PATH, 'node_modules', packageName);
}

function _ref4(res) {
  return res.ok ? res.json() : undefined;
}

function _ref5(e) {
  return undefined;
}

function _ref6(res) {
  return res.ok ? res.json() : undefined;
}

function _ref7(e) {
  return undefined;
}

function* _ref10(pluginPath) {
  let plugin = yield (0, _pluginManagerUtils.readPlugin)(pluginPath);
  if (plugin.enabled && !window.isSafeMode) {
    plugin = yield (0, _pluginManagerUtils.enablePlugin)(plugin);
  }
  return plugin;
}

function _ref11() {
  var _ref2 = _asyncToGenerator(_ref10);

  return function (_x2) {
    return _ref2.apply(this, arguments);
  };
}

function _ref12(plugin) {
  return plugin.isInstalled;
}

function _ref13(plugin) {
  return plugin.packageName;
}

function _ref14(plugin) {
  return plugin.isRead;
}

function _ref15(plugin) {
  return !plugin.isRead;
}

function _ref16(plugin) {
  return plugin.isBroken;
}

function _ref17(err) {
  return console.error(err.stack);
}

function _ref20(plugin) {
  return plugin.enabled;
}

function _ref21(plugin) {
  return plugin.packageName;
}

function _ref22(plugin) {
  return !plugin.enabled;
}

function _ref23(plugin) {
  return plugin.packageName;
}

class PluginManager extends _events2.default {
  constructor(packagePath, pluginRoot, mirrorPath) {
    var _this;

    _this = super(packagePath, pluginRoot, mirrorPath);

    function* _ref8(plugin) {
      // If needRollback, then we don't need the latest version; we instead
      // display the version it should be rolled back to
      if (plugin.needRollback) {
        return;
      }
      const data = yield yield (0, _nodeFetch2.default)(`${_this.config.mirror.server}${plugin.packageName}/latest`, defaultFetchOption).then(_ref4).catch(_ref5);
      if (!data || !data.version) {
        console.warn(`Can't find update info of plugin ${plugin.packageName}`);
        return;
      }

      const distTag = {
        latest: data.version
      };
      if (_this.config.betaCheck) {
        const betaData = yield (0, _nodeFetch2.default)(`${_this.config.mirror.server}${plugin.packageName}/beta`, defaultFetchOption).then(_ref6).catch(_ref7);
        if (betaData && betaData.version) {
          Object.assign(distTag, {
            beta: betaData.version
          });
        }
      }
      let latest = `${plugin.version}`;
      let notCompatible = false;
      const apiVer = (data.poiPlugin || {}).apiVer || plugin.apiVer || {};
      let nearestCompVer = 'v214.748.3647';
      for (const mainVersion of Object.keys(apiVer)) {
        if (!apiVer[mainVersion]) {
          continue;
        }
        if (_semver2.default.lte(window.POI_VERSION, mainVersion) && _semver2.default.lt(mainVersion, nearestCompVer)) {
          notCompatible = true;
          nearestCompVer = mainVersion;
          latest = apiVer[mainVersion];
        }
      }
      if (!notCompatible && _this.config.betaCheck && distTag.beta) {
        if (_semver2.default.gt(distTag.beta, latest)) {
          latest = distTag.beta;
        }
      }
      if (!notCompatible && _semver2.default.gt(distTag.latest, latest)) {
        latest = distTag.latest;
      }
      if (_semver2.default.gt(latest, plugin.version)) {
        dispatch({
          type: '@@Plugin/changeStatus',
          value: plugin,
          option: [{
            path: 'isOutdated',
            status: true
          }, {
            path: 'latestVersion',
            status: latest
          }]
        });
        return plugin;
      }
    }

    this.getPluginOutdateInfo = (() => {
      var _ref = _asyncToGenerator(_ref8);

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

    this.packagePath = packagePath;
    this.pluginRoot = pluginRoot;
    this.mirrorPath = mirrorPath;
    this.requirements = null;
    this.mirrors = null;
    this.config = {
      production: true,
      mirror: null,
      proxy: null,
      betaCheck: null
    };
    this.npmConfig = {
      prefix: this.pluginRoot,
      registry: "https://registry.npmjs.org",
      progress: false
    };
    this.VALID = 0;
    this.DISABLED = 1;
    this.NEEDUPDATE = 2;
    this.BROKEN = 3;

    this.getMirrors();
    this.loadConfig();
  }
  getPluginPath(packageName) {
    return (0, _pathExtra.join)(this.pluginRoot, 'node_modules', packageName);
  }
  initialize() {
    var _this2 = this;

    return _asyncToGenerator(function* () {
      _this2.getConf();
      yield _this2.getPlugins();
      _this2.emit('initialized');
    })();
  }
  readPlugins() {
    var _this3 = this;

    function _ref9(res) {
      return (0, _glob2.default)(_this3.getPluginPath('poi-plugin-*'), function (err, files) {
        return res(files);
      });
    }

    return _asyncToGenerator(function* () {
      const pluginPaths = yield new Promise(_ref9);
      let plugins = yield Promise.all(pluginPaths.map(_ref11()));
      plugins = (0, _lodash.sortBy)(plugins, 'priority');
      (0, _pluginManagerUtils.notifyFailed)(plugins, _this3.npmConfig);
      dispatch({
        type: '@@Plugin/initialize',
        value: plugins
      });
    })();
  }
  getRequirements() {
    if (!this.requirements) this.requirements = (0, _fsExtra.readJsonSync)(this.packagePath);
    return this.requirements;
  }
  getMirrors() {
    if (!this.mirrors) {
      this.mirrors = (0, _fsExtra.readJsonSync)(this.mirrorPath);
    }
    return this.mirrors;
  }
  loadConfig() {
    const mirrorConf = config.get('packageManager.mirrorName');
    const mirrorName = Object.keys(this.mirrors).includes(mirrorConf) ? mirrorConf : navigator.language === 'zh-CN' ? "taobao" : "npm";
    const proxyConf = config.get("packageManager.proxy", false);
    const betaCheck = config.get("packageManager.enableBetaPluginCheck", false);
    this.selectConfig(mirrorName, proxyConf, betaCheck, false);

    return this.mirrors;
  }
  selectConfig(name, enable, check) {
    if (name) {
      this.config.mirror = this.mirrors[name];
      config.set("packageManager.mirrorName", name);
    }
    if (enable != null) {
      this.config.proxy = enable;
      config.set("packageManager.proxy", enable);
    }
    if (check != null) {
      this.config.betaCheck = check;
      config.set("packageManager.enableBetaPluginCheck", check);
    }
    this.npmConfig.registry = this.config.mirror.server;
    if (this.config.proxy) {
      const { port } = proxy;
      this.npmConfig.http_proxy = `http://127.0.0.1:${port}`;
    } else {
      if (this.npmConfig.http_proxy) {
        delete this.npmConfig.http_proxy;
      }
    }
    return this.config;
  }
  isMetRequirement(plugin) {
    let lowest;
    if (!plugin.isRead) {
      return false;
    }
    if ((this.requirements[plugin.packageName] || {}).version) {
      lowest = this.requirements[plugin.packageName].version;
    } else {
      lowest = 'v0.0.0';
    }
    return _semver2.default.gte(plugin.packageData.version, lowest);
  }
  isEnabled(plugin) {
    if (!plugin.isRead) {
      return false;
    }
    return plugin.enabled;
  }
  isValid(plugin) {
    if (!plugin.isRead) {
      return false;
    }
    if (!plugin.isInstalled) {
      return false;
    }
    if (!this.isEnabled(plugin)) {
      return false;
    }
    return this.isMetRequirement(plugin);
  }
  getStatusOfPlugin(plugin) {
    if (plugin.isBroken || plugin.needRollback) {
      return this.BROKEN;
    }
    if (!plugin.isRead) {
      return this.DISABLED;
    }
    if (!this.isMetRequirement(plugin)) {
      return this.NEEDUPDATE;
    }
    if (!this.isEnabled(plugin)) {
      return this.DISABLED;
    }
    return this.VALID;
  }
  getPlugins() {
    var _this4 = this;

    return _asyncToGenerator(function* () {
      if (getStore('plugins').length > 0) {
        return getStore('plugins');
      } else {
        return yield _this4.readPlugins();
      }
    })();
  }
  getConf() {
    return this.config;
  }
  getInstalledPlugins() {
    return this.getFilteredPlugins(_ref12);
  }
  getUninstalledPluginSettings() {
    this.getRequirements();
    const installedPlugins = this.getInstalledPlugins();
    const installedPluginNames = installedPlugins.map(_ref13);
    const uninstalled = {};
    for (const name in this.requirements) {
      const value = this.requirements[name];
      if (!installedPluginNames.includes(name)) {
        uninstalled[name] = value;
      }
    }
    return uninstalled;
  }
  getReadPlugins() {
    return this.getFilteredPlugins(_ref14);
  }
  getUnreadPlugins() {
    return this.getFilteredPlugins(_ref15);
  }
  getBrokenPlugins() {
    return this.getFilteredPlugins(_ref16);
  }
  getValidPlugins() {
    return this.getFilteredPlugins(this.isValid.bind(this));
  }
  getMetRequirementPlugins() {
    return this.getFilteredPlugins(this.isMetRequirement.bind(this));
  }
  getFilteredPlugins(filter) {
    return getStore('plugins').filter(filter);
  }
  getUpdateStatus() {
    for (const i in getStore('plugins')) {
      if (getStore('plugins')[i].isOutdated) {
        return true;
      }
    }
    return false;
  }

  // Resolves the latest plugin if need update
  // Resolves undefined if not


  getOutdatedPlugins(isNotif) {
    var _this5 = this;

    function _ref18(plugin) {
      return _this5.getPluginOutdateInfo(plugin).catch(_ref17);
    }

    return _asyncToGenerator(function* () {
      const plugins = _this5.getInstalledPlugins();
      const outdatedList = (yield Promise.all(plugins.map(_ref18))).filter(Boolean);
      if (isNotif && outdatedList.length > 0) {
        const content = `${(0, _lodash.map)(outdatedList, 'name').join(' / ')} ${__("have newer version. Please update your plugins.")}`;
        toast(content, {
          type: 'info',
          title: __('Plugin update')
        });
      }
    })();
  }

  // possible options:
  //   skipEnable: true | false, skips enabling the plugin after installation
  installPlugin(packageSource, version, options = {}) {
    var _this6 = this;

    function _ref19() {
      try {
        (0, _fsExtra.accessSync)(packageSource);
        return false;
      } catch (e) {
        return true;
      }
    }

    return _asyncToGenerator(function* () {
      if (packageSource.includes('@')) [packageSource, version] = packageSource.split('@');

      // 1) See if it is installed by plugin name
      const installingByPluginName = _ref19();
      if (installingByPluginName) dispatch({
        type: '@@Plugin/changeStatus',
        value: { packageName: packageSource },
        option: [{ path: 'isUpdating', status: true }]
      });

      // 2) Install plugin
      try {
        yield (0, _pluginManagerUtils.installPackage)(packageSource, version, _this6.npmConfig);
      } catch (e) {
        console.error(e.stack);
        throw e;
      }

      // 3) Get plugin name
      const packageName = installingByPluginName ? packageSource : yield (0, _pluginManagerUtils.findInstalledTarball)((0, _pathExtra.join)(_this6.pluginRoot, 'node_modules'), packageSource);

      // 4) Unload plugin if it's running
      const nowPlugin = getStore('plugins').find(function (plugin) {
        return plugin.packageName === packageName;
      });
      if (nowPlugin) {
        try {
          (0, _pluginManagerUtils.unloadPlugin)(nowPlugin);
        } catch (error) {
          console.error(error.stack);
        }
      }
      // 5) Read plugin and load it
      try {
        let plugin = yield (0, _pluginManagerUtils.readPlugin)(_this6.getPluginPath(packageName));
        if (plugin.enabled || !(0, _lodash.get)(options, 'skipEnable', false)) {
          plugin = yield (0, _pluginManagerUtils.enablePlugin)(plugin, false);
        }
        dispatch({
          type: '@@Plugin/add',
          value: plugin
        });
      } catch (error) {
        if (nowPlugin) dispatch({
          type: '@@Plugin/changeStatus',
          value: nowPlugin,
          option: [{ path: 'isUpdating', status: false }]
        });
        console.error(error.stack);
        throw error;
      }
    })();
  }

  uninstallPlugin(plugin) {
    var _this7 = this;

    return _asyncToGenerator(function* () {
      try {
        dispatch({
          type: '@@Plugin/changeStatus',
          value: plugin,
          option: [{
            path: 'isUninstalling',
            status: true
          }]
        });
        _this7.removePlugin(plugin);
      } catch (error) {
        console.error(error.stack);
      }
      try {
        yield (0, _pluginManagerUtils.removePackage)(plugin.packageName, _this7.npmConfig);
        // Make sure the plugin no longer exists in PLUGIN_PATH
        // (unless it's a git repo)
        yield (0, _pluginManagerUtils.safePhysicallyRemove)(defaultPluginPath(plugin.packageName));
      } catch (error) {
        console.error(error.stack);
      }
    })();
  }

  gracefulRepair(repair = true) {
    var _this8 = this;

    function _ref24(plugin) {
      return _this8.uninstallPlugin(plugin);
    }

    function _ref25(plugin) {
      return _this8.installPlugin(plugin);
    }

    function _ref26(plugin) {
      return _this8.installPlugin(plugin, null, { skipEnable: true });
    }

    return _asyncToGenerator(function* () {
      const plugins = _this8.getInstalledPlugins();
      const enabledPlugins = (0, _lodash2.default)(plugins).filter(_ref20).map(_ref21).value();
      const disabledPlugins = (0, _lodash2.default)(plugins).filter(_ref22).map(_ref23).value();
      const modulePath = (0, _pathExtra.join)(PLUGIN_PATH, 'node_modules');
      yield (0, _pluginManagerUtils.safePhysicallyRemove)(modulePath);
      yield (0, _fsExtra.ensureDir)(modulePath);
      yield Promise.all(plugins.map(_ref24));

      if (!repair) {
        return;
      }

      yield Promise.all(enabledPlugins.map(_ref25));
      yield Promise.all(disabledPlugins.map(_ref26));
    })();
  }

  enablePlugin(plugin) {
    return _asyncToGenerator(function* () {
      plugin.enabled = true;
      if (!plugin.isBroken) {
        plugin = yield (0, _pluginManagerUtils.enablePlugin)(plugin);
      }
      config.set(`plugin.${plugin.id}.enable`, true);
      dispatch({
        type: '@@Plugin/add',
        value: plugin
      });
    })();
  }

  disablePlugin(plugin) {
    return _asyncToGenerator(function* () {
      config.set(`plugin.${plugin.id}.enable`, false);
      plugin = yield (0, _pluginManagerUtils.disablePlugin)(plugin);
      dispatch({
        type: '@@Plugin/add',
        value: plugin
      });
    })();
  }

  removePlugin(plugin) {
    try {
      plugin = (0, _pluginManagerUtils.unloadPlugin)(plugin);
    } catch (error) {
      console.error(error.stack);
    }
    dispatch({
      type: '@@Plugin/remove',
      value: plugin
    });
  }
}

const pluginManager = new PluginManager((0, _pathExtra.join)(ROOT, 'assets', 'data', 'plugin.json'), PLUGIN_PATH, (0, _pathExtra.join)(ROOT, 'assets', 'data', 'mirror.json'));

function* _ref27(pkgName, verbose = false) {
  const { plugins } = getStore();
  const plugin = plugins.find(function (pkg) {
    return [pkgName, `poi-plugin-${pkgName}`].includes(pkg.packageName);
  });
  if (!plugin) {
    console.error(`plugin "${pkgName}" not found`);
    return;
  }
  yield pluginManager.disablePlugin(plugin);
  if (verbose)
    // eslint-disable-next-line no-console
    console.log(`plugin "${plugin.id}" disabled, re-enabling...`);
  yield pluginManager.enablePlugin(plugin);
  if (verbose)
    // eslint-disable-next-line no-console
    console.log(`plugin "${plugin.id}" enabled.`);
}

window.reloadPlugin = (() => {
  var _ref3 = _asyncToGenerator(_ref27);

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

window.gracefulResetPlugin = () => pluginManager.gracefulRepair(false);

_electron.remote.getCurrentWebContents().once('dom-ready', () => pluginManager.initialize());

exports.default = pluginManager;
module.exports = exports['default'];