'use strict';

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

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

var _fsExtra2 = _interopRequireDefault(_fsExtra);

var _findKey = require('find-key');

var _findKey2 = _interopRequireDefault(_findKey);

var _md = require('md5');

var _md2 = _interopRequireDefault(_md);

var _unzip = require('unzip2');

var _unzip2 = _interopRequireDefault(_unzip);

var _jsYaml = require('js-yaml');

var _jsYaml2 = _interopRequireDefault(_jsYaml);

var _axios = require('axios');

var _axios2 = _interopRequireDefault(_axios);

var _readdirp = require('readdirp');

var _readdirp2 = _interopRequireDefault(_readdirp);

var _mkdirp = require('mkdirp');

var _mkdirp2 = _interopRequireDefault(_mkdirp);

var _path = require('path');

var _path2 = _interopRequireDefault(_path);

var _formData = require('form-data');

var _formData2 = _interopRequireDefault(_formData);

var _archiver = require('archiver');

var _archiver2 = _interopRequireDefault(_archiver);

var _bluebird = require('bluebird');

var _bluebird2 = _interopRequireDefault(_bluebird);

var _es6TemplateStrings = require('es6-template-strings');

var _es6TemplateStrings2 = _interopRequireDefault(_es6TemplateStrings);

var _lodash = require('lodash');

var _lodash2 = _interopRequireDefault(_lodash);

var _sourceMap = require('source-map');

var _sourceMap2 = _interopRequireDefault(_sourceMap);

var _debug = require('../debug');

var _debug2 = _interopRequireDefault(_debug);

var _session = require('../session');

var _session2 = _interopRequireDefault(_session);

var _utils = require('./utils');

var _utils2 = _interopRequireDefault(_utils);

var _hosting = require('../hosting');

var _hosting2 = _interopRequireDefault(_hosting);

var _registry = require('../registry');

var _registry2 = _interopRequireDefault(_registry);

var _compile = require('../compile');

var _printTools = require('../print-tools');

var _Constants = require('../../constants/Constants');

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

function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new _bluebird2.default(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 _bluebird2.default.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }

const { debug } = (0, _debug2.default)('utils-sockets');

class MetadataObject {
  constructor(name, metadata, socketName) {
    this.name = name;
    this.metadata = metadata;
    this.socketName = socketName;
    this.existRemotely = null;
    this.existLocally = null;
    this.isProjectRegistryDependency = null;
  }
  getStatus() {
    if (this.existLocally && this.existRemotely) {
      return { status: 'synced', type: 'ok' };
    }

    if (this.existLocally && !this.existRemotely) {
      return { status: 'not synced', type: 'warn' };
    }

    if (!this.existLocally && this.existRemotely) {
      return { status: 'no local configuration', type: 'fail' };
    }
  }
}

class Endpoint extends MetadataObject {

  call(params) {
    return _axios2.default.request({
      url: this.getURL(),
      method: 'POST',
      timeout: 3000,
      params,
      // Do not transform data automaticaly
      transformResponse: data => data
    });
  }

  getFullName() {
    return `${this.socketName}/${this.name}`;
  }

  getURL() {
    return `https://${_session2.default.getSpaceHost()}/${this.socketName}/${this.name}/`;
  }
}

class Handler extends MetadataObject {}

class Event extends MetadataObject {}

class Socket {
  constructor(socketName, socketPath) {
    debug('Sockets.constructor', socketName);
    this.name = socketName;
    this.settings = { loaded: false };
    this.socketPath = socketPath || _utils2.default.findLocalPath(socketName);

    if (this.socketPath) {
      this.settings = _session2.default.settings.getSocketSettings(this.socketPath, this.name);
    }

    this.existRemotely = null;
    this.existLocally = null;
    this.isProjectRegistryDependency = null;
    this.dependencies = [];
    this.dependencyOf = [];

    // that looks stupid
    this.remote = { spec: { endpoints: {}, event_handlers: {}, events: {} } };
    this.spec = { spec: { endpoints: {}, event_handlers: {}, events: {} } };

    this.loadLocal();
  }

  static getTemplatesChoices() {
    return _utils2.default.getTemplatesChoices();
  }

  // Install / Uninstall
  static add(registrySocket) {
    debug('add()');
    _session2.default.settings.project.installSocket(registrySocket);
  }

  // Install / Uninstall
  static install(registrySocket, config) {
    debug('install()');

    const socketName = registrySocket.name;
    _session2.default.settings.project.installSocket(registrySocket);

    const socketFolder = _path2.default.join(_session2.default.getBuildPath(), socketName);
    _mkdirp2.default.sync(socketFolder);

    debug('Getting socket from registry');
    return _registry2.default.getSocket(registrySocket, config).then(resp => {
      debug('Registry socket:', registrySocket);
      const fileName = _path2.default.join(_session2.default.getBuildPath(), `${socketName}.zip`);

      return new _bluebird2.default((resolve, reject) => {
        _fsExtra2.default.createReadStream(fileName).pipe(_unzip2.default.Extract({ path: socketFolder })).on('close', () => {
          debug('Unzip finished');
          const newSocket = new Socket(socketName, socketFolder);
          return Socket.updateSocketNPMDeps(socketFolder).then(() => newSocket.loadRemote()).then(() => newSocket.update({ config, withCompilation: true })).then(updateStatus => {
            debug('updateStatus', updateStatus);
            newSocket.updateStatus = updateStatus;
            resolve(newSocket);
          }).catch(err => reject(err));
        });
      });
    });
  }

  static uninstall(socket = {}) {
    debug('uninstall');

    if (socket.existLocally && socket.localPath) {
      Socket.uninstallLocal(socket);
      return _bluebird2.default.resolve();
    }

    if (socket.existRemotely) {
      return this.uninstallRemote(socket.name);
    }

    return _bluebird2.default.reject();
  }

  static uninstallLocal(socket) {
    _session2.default.settings.project.uninstallSocket(socket.name);
    _fsExtra2.default.removeSync(socket.localPath);
    (0, _printTools.echo)(4)(`Socket ${socket.name} has been deleted from config...`);
  }

  // TODO: check if the socket is installed (it may be not yet installed yet (before sync))
  static uninstallRemote(socketName) {
    return _session2.default.connection.Socket.please().delete({ name: socketName }).then(response => {
      (0, _printTools.echo)(4)(`Socket ${socketName} has been deleted from server...`);
      return response;
    }).catch(err => err);
  }

  // list sockets based on call to Syncano (sockets are installed on Synano)
  static listRemote() {
    debug('listRemote()');
    return _session2.default.connection.Socket.please().list();
  }

  static listDeps() {
    // List project dependencies
    return Object.keys(_session2.default.settings.project.getDependSockets());
  }

  // list all sockets (mix of locally definde and installed on server)
  static list() {
    return _asyncToGenerator(function* () {
      debug('list()');
      // Local Socket defined in folders and in project deps
      const localSocketsList = _utils2.default.listLocal().concat(Socket.listDeps());
      return _bluebird2.default.all(localSocketsList.map(function (socketName) {
        return Socket.get(socketName);
      }));
    })();
  }

  static flatList(socketName) {
    return _asyncToGenerator(function* () {
      debug('flatList()');
      const sockets = [];

      const addToList = function (socket) {
        const duplicated = _lodash2.default.find(sockets, function (s) {
          return s.name === socket.name;
        });
        if (duplicated) {
          if (!_lodash2.default.includes(duplicated.dependencyOf, socket.name)) {
            duplicated.dependencyOf.concat(socket.dependencyOf);
          }
        } else {
          sockets.push(socket);
          socket.dependencies.forEach(function (dep) {
            addToList(dep);
          });
        }
      };

      if (socketName) {
        return Socket.get(socketName).then(function (socket) {
          addToList(socket);
          return sockets;
        });
      }

      // All Sockets
      return Socket.list().then(function (allSockets) {
        allSockets.forEach(function (socket) {
          addToList(socket);
        });
        return sockets;
      });
    })();
  }

  // Creating Socket simple object
  static getLocal(socketName) {
    return new Socket(socketName);
  }

  static get(socketName) {
    debug(`Getting Socket: ${socketName}`);
    const socket = Socket.getLocal(socketName);
    return socket.loadRemote().then(loadedSocket => loadedSocket.getDepsRegistrySockets()).then(() => {
      if (!socket.existLocally) {
        return socket.loadFromRegistry();
      }
      return _bluebird2.default.resolve();
    }).then(() => socket);
  }

  static create(socketName, templateName) {
    debug('create socket');
    const newSocketPath = _path2.default.join(_session2.default.projectPath, socketName);
    const socket = new Socket(socketName, newSocketPath);
    if (socket.existLocally) {
      return _bluebird2.default.reject('Socket with that name already exist!');
    }
    return socket.init(templateName);
  }

  static publish(socketName, version) {
    const registry = new _registry2.default();
    return registry.publishSocket(socketName, version);
  }

  init(templateName) {
    return new _bluebird2.default((resolve, reject) => {
      const socketPath = this.getSocketPath();
      if (!_fsExtra2.default.existsSync(socketPath)) {
        _mkdirp2.default.sync(socketPath);
      }

      try {
        const templateFolder = _path2.default.normalize((0, _Constants.getSocketTemplate)(templateName));
        const files = _fsExtra2.default.walkSync(templateFolder);
        files.forEach(file => {
          const oldContent = _fsExtra2.default.readFileSync(file, 'utf8');
          const socket = {
            socketName: this.name,
            socketDescription: `Description of ${this.name}`
          };

          const newContent = (0, _es6TemplateStrings2.default)(oldContent, socket, { partial: true });
          const fileToSave = _path2.default.join(socketPath, file.replace(templateFolder, ''));

          _mkdirp2.default.sync(_path2.default.parse(fileToSave).dir);
          _fsExtra2.default.writeFileSync(_path2.default.join(socketPath, file.replace(templateFolder, '')), newContent);
        });
        resolve(this);
      } catch (err) {
        return reject(err);
      }
    });
  }

  getFullConfig() {
    return this.settings.getFull();
  }

  getRemote() {
    return _session2.default.connection.Socket.please().get({ name: this.name }).then(socket => socket).catch(err => false);
  }

  getRemoteSpec() {
    debug('getRemoteSpec');
    if (this.remote.files['socket.yml']) {
      return _axios2.default.request({
        url: this.remote.files['socket.yml'].file,
        method: 'GET',
        timeout: 3000
      }).then(spec => {
        this.remote.spec = _jsYaml2.default.load(spec.data);
      });
    }
    return _bluebird2.default.resolve();
  }

  setRemoteState(socket) {
    this.existRemotely = true;
    this.remote.name = socket.name;
    this.remote.version = socket.version;
    this.remote.updatedAt = socket.updated_at;
    this.remote.installed = socket.installed;
    this.remote.files = socket.files;
    this.remote.status = socket.status;
    this.remote.statusInfo = socket.status_info;
    this.remote.config = socket.config;
  }

  loadRemote() {
    debug('loadRemote()');
    return this.getRemote().then(socket => {
      if (socket) {
        this.setRemoteState(socket);
        return this.getRemoteSpec();
      }

      this.existRemotely = false;
      return _bluebird2.default.resolve(this);
    }).then(() => this);
  }

  loadFromRegistry() {
    var _this = this;

    return _asyncToGenerator(function* () {
      debug(`loadFromRegistry: ${_this.name}`);
      const registry = new _registry2.default();
      const registrySocket = yield registry.getFullSocket(_this.name);
      _this.spec = registrySocket.config;
      _this.url = registrySocket.url;
    })();
  }

  loadLocal() {
    debug('loadLocal');
    if (this.settings.loaded) {
      this.existLocally = true;
      this.localPath = this.settings.baseDir;
      this.spec = this.settings.getFull();
    } else if (_lodash2.default.includes(Socket.listDeps(), this.name)) {
      this.isProjectRegistryDependency = true;
      this.existLocally = false;
      this.spec.version = _session2.default.settings.project.getDependSocket(this.name).version;
    }
  }

  isSocketFile(fileFullPath) {
    return fileFullPath.includes(this.localPath);
  }

  getRawStatus() {
    return {
      isProjectRegistryDependency: this.isProjectRegistryDependency,
      existRemotely: this.existRemotely,
      existLocally: this.existLocally
    };
  }

  getStatus() {
    const msg = this.remote.statusInfo || this.remote.status;

    if (this.isProjectRegistryDependency && !this.existRemotely) {
      return { status: 'not synced', type: 'warn' };
    }

    if (this.existLocally && !this.existRemotely) {
      return { status: 'not synced', type: 'warn' };
    }

    if (this.remote.status === 'ok') {
      return { status: msg, type: 'ok' };
    } else if (this.remote.status === 'processing') {
      return { status: msg, type: 'warn' };
    }
    return { status: msg, type: 'fail' };
  }

  getType() {
    if (this.isProjectRegistryDependency) {
      return { msg: 'project dependency', type: 'ok' };
    }

    if (this.isDependencySocket) {
      return { msg: `dependency of ${this.dependencyOf.join(', ')}`, type: 'ok' };
    }

    if (this.existLocally) {
      return { msg: 'local socket', type: 'ok' };
    }
  }

  getVersion() {
    return this.remote ? this.remote.version : null;
  }

  getScripts() {
    return (0, _findKey2.default)(this.spec, 'file');
  }

  getSocketZip() {
    debug('getSocketZip');
    return _path2.default.join(_session2.default.getDistPath(), `${this.name}.zip`);
  }

  getSocketConfigFile() {
    return _path2.default.join(_session2.default.projectPath, this.name, 'socket.yml');
  }

  composeFromSpec(objectType, ObjectClass) {
    debug('composeFromSpec', objectType, ObjectClass);
    const objects = Object.assign({}, this.remote.spec[objectType]);
    Object.assign(objects, this.spec[objectType]);

    debug('objects to process', objects);
    return Object.keys(objects).map(objectName => {
      debug(`checking ${objectName}`);
      const objectMetadata = objects[objectName];
      debug('objectMetadata', objectMetadata);
      const object = new ObjectClass(objectName, objectMetadata, this.name);

      debug('existRemotely', this.remote.spec[objectType], objectName);
      if (this.remote.spec[objectType][objectName]) {
        debug(`existRemotely: ${true}`);
        object.existRemotely = true;
      }

      debug('existLocally', this.spec[objectType], objectName);
      if (this.spec[objectType][objectName]) {
        object.existLocally = true;
      }
      return object;
    });
  }

  getEndpoints() {
    debug('getEndpoints');
    return this.composeFromSpec('endpoints', Endpoint);
  }

  getEndpoint(endpointName) {
    debug('getEndpoints');
    return _lodash2.default.find(this.getEndpoints(), { name: endpointName });
  }

  getEventHandlers() {
    debug('getEventHandlers');
    return this.composeFromSpec('event_handlers', Handler);
  }

  getEvents() {
    debug('getEvents');
    return this.composeFromSpec('events', Event);
  }

  getEndpointTrace(endpointName, traceId) {
    return _session2.default.connection.EndpointTrace.please().get({ socketName: this.name, endpointName, id: traceId });
  }

  getEndpointTraces(endpointName, lastId) {
    debug('getEndpointTraces', endpointName, lastId);
    return _session2.default.connection.EndpointTrace.please().list({ socketName: this.name, endpointName }).then(traces => {
      if (!lastId) {
        return traces;
      }
      const filteredTraces = [];
      traces.forEach(trace => {
        if (trace.id > lastId) {
          filteredTraces.push(trace);
        }
      });
      return filteredTraces;
    });
  }

  getTraces(lastId) {
    const params = { room: `socket:${this.name}` };
    if (lastId) {
      params.last_id = lastId;
    }

    return _axios2.default.request({
      url: `https://${_session2.default.getHost()}/v2/instances/${_session2.default.project.instance}/channels/eventlog/poll/`,
      method: 'GET',
      timeout: 50000,
      params,
      headers: {
        'X-Api-Key': _session2.default.settings.account.getAuthKey()
      }
    });
  }

  static getEndpointTraceByUrl(url) {
    return _axios2.default.request({
      url: `https://${_session2.default.getHost()}${url}`,
      method: 'GET',
      headers: {
        'X-Api-Key': _session2.default.settings.account.getAuthKey()
      }
    }).then(resp => resp.data);
  }

  createZip({ plainSources = false }) {
    debug('createZip');
    return new _bluebird2.default((resolve, reject) => {
      let numberOfFiles = 0;
      const output = _fsExtra2.default.createWriteStream(this.getSocketZip(), { mode: 0o700 });
      const archive = (0, _archiver2.default)('zip', { zlib: { level: 9 } });

      archive.pipe(output);
      archive.on('error', reject);
      archive.on('entry', () => {
        numberOfFiles += 1;
      });

      function fileFilter(file) {
        const remoteFiles = this.remote.files;
        if (file.fullPath.match(/yarn\.lock/)) {
          return false;
        }
        if (file.fullPath.match(/node_modules/)) {
          return false;
        }
        if (file.fullPath.match(/\.js\.map$/)) {
          return false;
        }
        if (!plainSources && file.fullPath.match(/\.js$/)) {
          return false;
        }
        if (file.path.match(/^\./)) {
          return false;
        }
        debug(`createZip: Checking file ${file.name}`);
        if (remoteFiles) {
          const remoteFile = remoteFiles[file.path];
          if (remoteFile) {
            return remoteFile.checksum !== (0, _md2.default)(_fsExtra2.default.readFileSync(file.fullPath));
          }
          return true;
        }
        return true;
      }

      const findFiles = (0, _readdirp2.default)({ root: this.getSocketPath(), fileFilter: fileFilter.bind(this) });

      // Adding all files (besides those filtered out)
      findFiles.on('data', file => {
        // with "internal" path
        const fileNameWithPath = file.fullPath.replace(`${this.getSocketPath()}${_path2.default.sep}`, '');
        debug(`Adding file to archive: ${fileNameWithPath}`);
        archive.file(file.fullPath, { name: fileNameWithPath });
      });

      if (!plainSources) {
        this.getScripts().forEach(scriptNameWithPath => {
          const { base, dir } = _path2.default.parse(scriptNameWithPath);
          const scriptBundlePath = _path2.default.join(this.getSocketPath(), dir, '.bundles', base);
          const remoteFile = this.remote.files ? this.remote.files[scriptNameWithPath] : null;

          if (remoteFile) {
            const scriptLocalChecksum = (0, _md2.default)(_fsExtra2.default.readFileSync(scriptBundlePath));
            const scriptRemoteChecksum = remoteFile.checksum;
            if (scriptLocalChecksum === scriptRemoteChecksum) {
              debug('Ignoring file:', scriptNameWithPath);
              return;
            }
          }

          debug(`Adding file to archive: ${scriptNameWithPath}`);
          archive.file(scriptBundlePath, { name: scriptNameWithPath });
        });
      }

      findFiles.on('end', () => {
        debug(`createZip: all files processed: ${numberOfFiles}`);
        archive.finalize();
        output.on('close', () => {
          resolve(numberOfFiles);
        });
      });
    });
  }

  zipCallback({ config, install = false }) {
    debug('zipCallback');
    let endpointPath = `/v2/instances/${_session2.default.project.instance}/sockets/`;

    if (!install) {
      endpointPath += `${this.name}/`;
    }

    return new _bluebird2.default((resolve, reject) => {
      const form = new _formData2.default();

      form.append('name', this.name);
      if (config) {
        form.append('config', JSON.stringify(config));
      }
      form.append('zip_file', _fsExtra2.default.createReadStream(this.getSocketZip()));
      debug('upload zip');
      form.submit({
        method: install ? 'POST' : 'PATCH',
        protocol: 'https:',
        host: _session2.default.getHost(),
        headers: {
          'X-Api-Key': _session2.default.settings.account.getAuthKey()
        },
        path: endpointPath

      }, (err, res) => {
        debug('end upload');
        res.on('data', data => {
          const message = data.toString();

          if (err || res.statusCode === 404) {
            debug(`socket ${this.name} was not found`);
            return reject(err || res);
          }

          if (res.statusCode > 299) {
            debug(`error while updating socket (${res.statusCode})`);
            return reject(message);
          }

          debug(`socket ${this.name} was found`);
          resolve(message);
        });
      });
    });
  }

  watchScripts(resolveCallback, rejectCallback) {
    debug('watchScripts');
    return (0, _compile.watch)([this], this.socketPath, resolveCallback, rejectCallback);
  }

  static watchScripts(sockets, resolveCallback, rejectCallback) {
    debug('watchScripts');
    return (0, _compile.watchBig)(sockets, _session2.default.projectPath, resolveCallback, rejectCallback);
  }

  getSocketPath() {
    if (this.isDependencySocket || this.isProjectRegistryDependency) {
      const socketFolder = _path2.default.join(_session2.default.getBuildPath(), this.name);
      _mkdirp2.default.sync(socketFolder);
      return socketFolder;
    }
    return this.socketPath;
  }

  compile() {
    debug(`compile: ${this.name}`);
    debug(`compile socketPath: ${this.getSocketPath()}`);

    let compilation = null;
    if (this.isDependencySocket || this.isProjectRegistryDependency) {
      compilation = _registry2.default.getSocket(this).then(resp => {
        const fileName = _path2.default.join(_session2.default.getBuildPath(), `${this.name}.zip`);

        return new _bluebird2.default((resolve, reject) => {
          _fsExtra2.default.createReadStream(fileName).pipe(_unzip2.default.Extract({ path: this.getSocketPath() })).on('close', () => {
            debug('Unzip finished');
            resolve(_utils2.default.updateSocketNPMDeps(this.getSocketPath()));
          });
        });
      });
    } else {
      compilation = _utils2.default.updateSocketNPMDeps(this.getSocketPath());
    }

    return compilation.then(updateSocketDepsStatus => {
      debug('updateSocketDepsStatus', updateSocketDepsStatus);
      const buildPath = _path2.default.dirname(this.getSocketPath());
      return (0, _compile.compile)([this], buildPath);
    });
  }

  isConfigSynced(config) {
    debug('isConfigSynced');
    return _lodash2.default.isEqual(config, this.remote.config);
  }

  updateConfig(config) {
    return _axios2.default.request({
      url: `https://${_session2.default.getHost()}/v2/instances/${_session2.default.project.instance}/sockets/${this.name}/`,
      method: 'PATCH',
      data: { config },
      headers: {
        'X-Api-Key': _session2.default.settings.account.getAuthKey()
      }
    }).then(response => response.data);
  }

  update(params = { config: null, withCompilation: false }) {
    debug(`Socket update: ${this.name}`, params);
    const config = Object.assign({}, this.remote.config, params.config);
    // const config = Object.assign({}, this.spec.config, this.remote.config, params.config);

    const doUpdate = () => this.createZip({}).then(numberOfFiles => {
      debug(`update: number of files in zip: ${numberOfFiles}`);
      if (numberOfFiles === 0) {
        if (!this.isConfigSynced(config)) {
          debug('update: Updating only config');
          return this.updateConfig(config);
        }
        debug('update: Nothing to update');
        return _bluebird2.default.resolve({ status: 'stopped' });
      }

      if (this.existRemotely) {
        return this.zipCallback({ config });
      }
      return this.zipCallback({ config, install: true });
    }).then(updateStatus => {
      if (updateStatus.status === 'stopped') {
        return _bluebird2.default.resolve({ status: 'stopped' });
      } else if (updateStatus.status === 'error') {
        return _bluebird2.default.resolve({
          status: updateStatus.status,
          message: updateStatus.status_info
        });
      }
      return this.waitForStatusInfo();
    });

    if (params.withCompilation) {
      return this.compile().then(() => doUpdate());
    }
    return doUpdate();
  }

  waitForStatusInfo() {
    debug('waitForStatusInfo');

    return new _bluebird2.default((resolve, reject) => {
      const getStatus = () => {
        this.getRemote().then(socket => {
          if (socket.status !== 'ok' && socket.status !== 'error') {
            setTimeout(getStatus, 500);
          } else {
            this.setRemoteState(socket);
            resolve({ status: socket.status, message: socket.status_info });
          }
        });
      };
      getStatus();
    });
  }

  // Hosting
  addHosting(hostingName, params) {
    this.settings.addHosting(hostingName, params);
    this.settings.save();
  }

  getHosting(hostingName) {
    return _hosting2.default.get(this, hostingName);
  }

  listHostings() {
    return _hosting2.default.list(this);
  }

  deleteHosting(hostingName) {
    this.settings.deleteHosting(hostingName);
    this.settings.save();
    (0, _printTools.echo)();
    (0, _printTools.echo)(4)(`Hosting ${hostingName} of ${this.name} has been deleted from config...`);
  }

  getEntryPoints() {
    debug('getEntryPoints');
    const entry = {};
    const scripts = this.getScripts();
    if (_lodash2.default.isEmpty(scripts)) {
      return entry;
    }

    const socketRelativePath = _path2.default.parse(this.getSocketPath()).name;
    _lodash2.default.forEach(scripts, script => {
      const [dir, filename] = script.split('/');
      const scriptSocketBundlePath = _path2.default.join(socketRelativePath, dir, '.bundles', filename);
      const scriptSocketPath = _path2.default.join(socketRelativePath, script);
      entry[scriptSocketBundlePath] = `./${scriptSocketPath}`;
    });
    return entry;
  }

  getSourceMapPath(endpointName) {
    const filePath = this.settings.getFileForEndpoint(endpointName);
    const { base, dir } = _path2.default.parse(filePath);
    return _path2.default.join(this.getSocketPath(), dir, '.bundles', `${base}.map`);
  }

  getOrigFileLine(traceData, endpointName) {
    const smc = new _sourceMap2.default.SourceMapConsumer(_fsExtra2.default.readFileSync(this.getSourceMapPath(endpointName), { encoding: 'utf-8' }));
    return smc.originalPositionFor({
      line: traceData.lineNumber,
      column: traceData.columnNumber
    });
  }

  getPrettyTrace(traceData, endpointName) {
    const origFileLine = this.getOrigFileLine(traceData, endpointName);
    const origFilePath = _utils2.default.getOrigFilePath(origFileLine);
    return {
      origFilePath,
      lineNumber: origFileLine.line,
      columnNumber: origFileLine.column,
      lines: [_fsExtra2.default.readFileSync(
      // origFilePath,
      _path2.default.join(this.getSocketPath(), '../', origFilePath), { encoding: 'utf-8' }).split('\n')[origFileLine.line - 1], (0, _printTools.p)(origFileLine.column)('^')]
    };
  }

  // Config
  getConfigOptions() {
    return this.settings.getConfigOptions();
  }

  getConfigOptionsToAsk() {
    // If there is not options in spec it is always no options to ask
    if (!this.spec.config) {
      return {};
    }

    const options = {};

    if (this.isDependencySocket || this.isProjectRegistryDependency) {
      if (this.remote.status === 'ok') {
        return options;
      }
      Object.keys(this.spec.config).forEach(optionName => {
        const option = this.spec.config[optionName];
        if (option.required) {
          options[optionName] = option;
        }
      });
      return options;
    }

    if (this.existLocally) {
      Object.keys(this.spec.config).forEach(optionName => {
        const option = this.spec.config[optionName];
        if (option.required && !this.remote.config[optionName]) {
          options[optionName] = option;
        }
      });
      return options;
    }
  }

  // Registry
  bumpVersion(bumpType) {
    const nextVersion = this.settings.bumpVersion(bumpType);
    this.spec.version = nextVersion;
    return nextVersion;
  }

  submit() {
    debug('submit');
    const registry = new _registry2.default();
    return registry.submitSocket(this);
  }

  // Socket dependencies
  getDeps() {
    return this.settings.getDependencies();
  }

  getDepsRegistrySockets() {
    debug(`getDepsRegistrySockets for: ${this.name}`);
    const registry = new _registry2.default();

    const getDeepDeps = (deps, socketToAdd) => {
      debug('getDeepDeps', deps);
      const sockets = [];
      if (socketToAdd) {
        sockets.push(socketToAdd);
      }
      return _bluebird2.default.all(sockets.concat(Object.keys(deps).map(dependencyName => {
        debug(`processing dependency: ${dependencyName}`);
        return registry.searchSocketByName(dependencyName, deps[dependencyName].version).then(socket => {
          // To keep track of the name of the parent
          // eslint-disable-next-line
          socket.dependencyOf = socketToAdd ? socketToAdd.name : this.name;

          if (!_lodash2.default.isEmpty(socket.config.dependencies)) {
            return getDeepDeps(socket.config.dependencies, socket);
          }
          debug(`returning socket without dependencies ${socket.name}`);
          return _bluebird2.default.resolve(socket);
        });
      })));
    };
    debug(`Defined dependencies of "${this.name}" to follow: ${this.spec.dependencies}`);
    if (this.spec.dependencies) {
      return getDeepDeps(this.spec.dependencies).then(arr => _bluebird2.default.all(_lodash2.default.flatten(arr).map(socket => Socket.get(socket.name).then(createdSocket => {
        // eslint-disable-next-line
        createdSocket.isDependencySocket = true;
        // eslint-disable-next-line
        createdSocket.dependencyOf = [socket.dependencyOf];
        return createdSocket;
      })))).then(depsObjects => {
        this.dependencies = depsObjects;
      });
    }

    return _bluebird2.default.resolve();
  }

  addDependency(socketFromRegistry) {
    const socketName = socketFromRegistry.name;
    const socketVersion = socketFromRegistry.version;
    this.settings.addDependency(socketName, socketVersion);
    return new _bluebird2.default((resolve, reject) => resolve());
  }

  shouldBeUpdated() {
    if (this.existLocally && this.existRemotely) {
      const files = this.remote.files;
      return Object.keys(files).some(file => {
        let filePath = _path2.default.join(this.getSocketPath(), file);
        if (file.endsWith('.js')) {
          const { base, dir } = _path2.default.parse(file);
          filePath = _path2.default.join(this.getSocketPath(), dir, '.bundles', base);
        }
        const localChecksum = (0, _md2.default)(_fsExtra2.default.readFileSync(filePath));
        const remoteChecksum = files[file].checksum;
        debug(file);
        debug(localChecksum);
        debug(remoteChecksum);
        return localChecksum !== remoteChecksum;
      });
    } else if (!this.existRemotely) {
      return true;
    } else if (this.isDependencySocket || this.isProjectRegistryDependency) {
      if (this.remote.status !== 'ok') {
        return false;
      }

      debug(`Spec version: ${this.spec.version}`);
      debug(`Remote version: ${this.getVersion()}`);
      if (this.spec.version === this.getVersion()) {
        return false;
      }
    }
  }
}

Socket.listLocal = _utils2.default.listLocal;

exports.default = Socket;
module.exports = exports['default'];
//# sourceMappingURL=sockets.js.map