'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.HostingFile = exports.default = undefined;

var _fs = require('fs');

var _fs2 = _interopRequireDefault(_fs);

var _chalk = require('chalk');

var _chalk2 = _interopRequireDefault(_chalk);

var _path = require('path');

var _path2 = _interopRequireDefault(_path);

var _prettyBytes = require('pretty-bytes');

var _prettyBytes2 = _interopRequireDefault(_prettyBytes);

var _lodash = require('lodash');

var _lodash2 = _interopRequireDefault(_lodash);

var _bluebird = require('bluebird');

var _bluebird2 = _interopRequireDefault(_bluebird);

var _md = require('md5');

var _md2 = _interopRequireDefault(_md);

var _axios = require('axios');

var _axios2 = _interopRequireDefault(_axios);

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

var _session2 = _interopRequireDefault(_session);

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

var _debug2 = _interopRequireDefault(_debug);

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

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

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-hosting');

class HostingFile {
  loadRemote(fileRemoteData) {
    this.id = fileRemoteData.id;
    this.instanceName = fileRemoteData.instanceName;
    this.path = fileRemoteData.path;
    this.checksum = fileRemoteData.checksum;
    this.size = fileRemoteData.size;
    return this;
  }
  loadLocal(fileLocalData) {
    this.localPath = fileLocalData.path;
    this.path = _path2.default.basename(this.localPath);
    this.checksum = (0, _md2.default)(_fs2.default.readFileSync(this.localPath));
    this.size = _fs2.default.statSync(this.localPath).size;
    return this;
  }
}

class Hosting {
  constructor(hostingName, socket) {
    debug('Hosting.constructor', hostingName, socket);

    this.name = hostingName;
    this.socket = socket || null;
    this.path = null;

    this.existRemotely = null;
    this.existLocally = null;

    this.hostingURL = `/v2/instances/${_session2.default.project.instance}/hosting/`;
    this.editHostingURL = `https://${_session2.default.getHost()}${this.hostingURL}${this.name}/`;
    this.hostingHost = _session2.default.getHost() === 'api.syncano.rocks' ? 'syncano.ninja' : 'syncano.site';

    this.loadLocal();
  }

  static add(params) {
    const configParams = {
      src: params.src
    };
    _session2.default.settings.project.addHosting(params.name, configParams);

    const hostingURL = `/v2/instances/${_session2.default.project.instance}/hosting/`;
    const addHostingURL = `https://${_session2.default.getHost()}${hostingURL}`;

    const domains = [params.name];
    if (params.cname) {
      domains.push(params.cname);
    }

    const paramsToAdd = {
      name: params.name,
      domains
    };

    return _axios2.default.request({
      url: addHostingURL,
      method: 'POST',
      data: paramsToAdd,
      headers: {
        'X-Api-Key': _session2.default.settings.account.getAuthKey()
      }
    }).then(response => response.data);
  }

  configure(params) {
    const domains = [this.name];
    if (params.cname) {
      domains.push(params.cname);
    }

    const paramsToUpdate = {
      name: this.name,
      domains
    };

    return _axios2.default.request({
      url: this.editHostingURL,
      method: 'PATCH',
      data: paramsToUpdate,
      headers: {
        'X-Api-Key': _session2.default.settings.account.getAuthKey()
      }
    }).then(response => {
      const hosting = response.data;
      return this.setRemoteState(hosting);
    });
  }

  deploy() {
    debug('deploy');
    if (!this.existRemotely) {
      debug('adding hosting');
      return Hosting.add({
        name: this.name,
        src: this.src
      });
    }

    debug('patching hosting');
    // TODO: not optimal
    const paramsToUpdate = {
      name: this.name,
      domains: [this.name]
    };

    return _axios2.default.request({
      url: this.editHostingURL,
      method: 'PATCH',
      data: paramsToUpdate,
      headers: {
        'X-Api-Key': _session2.default.settings.account.getAuthKey()
      }
    }).then(response => {
      const hosting = response.data;
      return this.setRemoteState(hosting);
    }).catch(err => {
      console.log(err);
    });
  }

  delete() {
    if (!this.socket) {
      return _axios2.default.request({
        url: this.editHostingURL,
        method: 'DELETE',
        headers: {
          'X-Api-Key': _session2.default.settings.account.getAuthKey()
        }
      }).then(() => {
        _session2.default.settings.project.deleteHosting(this.name);
      });
    }
  }

  static get(hostingName, socket) {
    debug(`get ${hostingName}`);
    const hosting = new Hosting(hostingName, socket);
    return hosting.loadRemote();
  }

  static listLocal(socket) {
    return socket.settings.listHosting();
  }

  static listFromProject() {
    return _session2.default.settings.project.listHosting();
  }

  // list all hostings (mix of locally definde and installed on server)
  static list(socket) {
    return _asyncToGenerator(function* () {
      debug('list()');
      // const localHostings = Hosting.listLocal(socket);
      if (!socket) {
        const projectHostings = Hosting.listFromProject();
        debug('projectHostings', projectHostings);
        const promises = projectHostings.map(function (hosting) {
          return Hosting.get(hosting.name);
        });
        return _bluebird2.default.all(promises);
      }
    })();
  }

  static getDirectories() {
    const excluded = ['node_modules', 'src', 'syncano'];

    function notExcluded(dirname) {
      if (dirname.startsWith('.')) {
        return;
      }
      if (excluded.indexOf(dirname) !== -1) {
        return;
      }
      return dirname;
    }

    return _fs2.default.readdirSync(process.cwd()).filter(file => {
      const dirs = [];
      if (_fs2.default.statSync(`${process.cwd()}/${file}`).isDirectory()) {
        dirs.push(file);
      }
      return dirs.find(notExcluded);
    });
  }

  setRemoteState(hosting) {
    var _this = this;

    return _asyncToGenerator(function* () {
      debug('setRemoteState', hosting);
      if (hosting && typeof hosting === 'object') {
        _this.existRemotely = true;
        _this.name = hosting.name;
        _this.description = hosting.description;
        _this.domains = hosting.domains;
        return _this.areFilesUpToDate().then(function (status) {
          _this.isUpToDate = status;
        });
      }
      _this.existRemotely = false;
      _this.error = hosting;
      return _bluebird2.default.resolve();
    })();
  }

  loadRemote() {
    var _this2 = this;

    debug('loadRemote()');
    return this.getRemote().then((() => {
      var _ref = _asyncToGenerator(function* (hosting) {
        return _this2.setRemoteState(hosting);
      });

      return function (_x) {
        return _ref.apply(this, arguments);
      };
    })()).then(() => this).catch(err => {
      this.existRemotely = false;
      return this;
    });
  }

  loadLocal() {
    debug('loadLocal()');
    let localHostingSettings = {};
    if (this.socket) {
      if (this.socket.settings.loaded) {
        localHostingSettings = this.socket.settings.getHosting(this.name);
      }
    } else {
      localHostingSettings = _session2.default.settings.project.getHosting(this.name);
    }

    if (!localHostingSettings) {
      return;
    }

    if (Object.keys(localHostingSettings).length > 0) {
      this.existLocally = true;
      this.src = localHostingSettings.src;
      this.cname = localHostingSettings.cname;
      this.path = _path2.default.join(_session2.default.projectPath, this.src, '/');
      this.url = this.getURL(this.name);
    }
  }

  getURL() {
    return `https://${this.name}--${_session2.default.project.instance}.${this.hostingHost}`;
  }

  getLocalFilePath(file) {
    debug('getLocalFilePath');
    if (!file) return null;

    return file.localPath.replace(this.path, '');
  }

  // Verify local file if it should be created or updated
  getFilesToUpload(file, remoteFiles) {
    debug('getFilesToUpload');
    const localHostingFilePath = this.getLocalFilePath(file);
    const fileToUpdate = _lodash2.default.find(remoteFiles, { path: localHostingFilePath });
    const payload = { file: _session2.default.connection.file(file.localPath), path: localHostingFilePath };
    const uploads = [];

    if (fileToUpdate) {
      const remoteChecksum = fileToUpdate.checksum;
      const localChecksum = file.checksum;

      // Check if checksum of the local file is the same as remote one
      if (remoteChecksum === localChecksum) {
        return _session2.default.connection.HostingFile.please().get({ id: fileToUpdate.id, hostingId: this.name }).then(singleFile => {
          (0, _printTools.echo)(6)(`${_chalk2.default.green('✓')} File skipped: ${_chalk2.default.dim(localHostingFilePath)}`);
          return singleFile;
        }).catch(err => (0, _printTools.error)(err));
      }
      uploads.push(_session2.default.connection.HostingFile.please().update({ id: fileToUpdate.id, hostingId: this.name }, payload).then(singleFile => {
        (0, _printTools.echo)(6)(`${_chalk2.default.green('✓')} File updated: ${_chalk2.default.dim(localHostingFilePath)}`);
        return singleFile;
      }).catch(err => {
        (0, _printTools.echo)(`Error while syncing (updating) ${localHostingFilePath}`);
      }));
    } else {
      // Adding (first upload) file
      uploads.push(_session2.default.connection.HostingFile.please().upload({ hostingId: this.name }, payload).then(singleFile => {
        (0, _printTools.echo)(6)(`${_chalk2.default.green('✓')} File added:   ${_chalk2.default.dim(localHostingFilePath)}`);
        return singleFile;
      }).catch(err => {
        (0, _printTools.echo)(`Error while syncing (creating) ${file.path}`);
      }));
    }

    return uploads;
  }

  // Files upload report
  generateUploadFilesResult(result) {
    if (!result) {
      return `\n\t${_chalk2.default.red('No files synchronized!')}\n`;
    }
    const prettyUploadSize = (0, _prettyBytes2.default)(result.uploadedSize);

    return `\n\t${_chalk2.default.cyan(result.uploadedFilesCount)} files synchronized, ${_chalk2.default.cyan(prettyUploadSize)} in total
    \t${_chalk2.default.green(this.name)} is available at: ${_chalk2.default.green(this.getURL())}\n`;
  }

  uploadFiles(files) {
    var _this3 = this;

    return _asyncToGenerator(function* () {
      let uploadedFilesCount = 0;
      let uploadedSize = 0;
      const promises = [];

      const localFiles = yield _this3.listLocalFiles();

      localFiles.map(function (file) {
        return promises.push(_this3.getFilesToUpload(file, files));
      });

      return _bluebird2.default.all(_lodash2.default.flattenDeep(promises)).then(function (values) {
        uploadedFilesCount = 0;
        uploadedSize = 0;
        values.forEach(function (upload) {
          uploadedFilesCount += 1;
          uploadedSize += upload.size;
        });
        return _bluebird2.default.resolve({ uploadedFilesCount, uploadedSize });
      });
    })();
  }

  // Run this to synchronize hosted files
  // first we are getting remote files
  syncFiles() {
    var _this4 = this;

    return _asyncToGenerator(function* () {
      debug('syncFiles()');
      const remoteFiles = yield _this4.listRemoteFiles();
      const result = yield _this4.uploadFiles(remoteFiles);
      const output = _this4.generateUploadFilesResult(result);
      (0, _printTools.echo)(output);

      return output;
    })();
  }

  areFilesUpToDate() {
    var _this5 = this;

    return _asyncToGenerator(function* () {
      debug('areFilesUpToDate()');
      const localChecksums = yield _this5.listLocalFiles().map(function (localFile) {
        return {
          filePath: _this5.getLocalFilePath(localFile),
          checksum: localFile.checksum
        };
      });

      const remoteChecksums = yield _this5.listRemoteFiles().map(function (remoteFile) {
        return {
          filePath: remoteFile.path,
          checksum: remoteFile.checksum
        };
      });

      const areFilesUpToDate = _lodash2.default.isEqual(_lodash2.default.sortBy(localChecksums, 'filePath'), _lodash2.default.sortBy(remoteChecksums, 'filePath'));
      return areFilesUpToDate;
    })();
  }

  // Get list of the hostings first, then get the files list for given one
  listRemoteFiles() {
    var _this6 = this;

    return _asyncToGenerator(function* () {
      debug('listRemoteFiles()');
      const files = _session2.default.connection.HostingFile.please().all({ hostingId: _this6.name });
      return new _bluebird2.default(function (resolve, reject) {
        files.on('stop', function (fetchedFiles) {
          return resolve(fetchedFiles.map(function (file) {
            return new HostingFile().loadRemote(file);
          }));
        });
      });
    })();
  }

  // Get info about hostings first, then get the files list for given one
  listLocalFiles() {
    var _this7 = this;

    return _asyncToGenerator(function* () {
      debug('listLocalFiles');
      const localHostingFiles = _this7.path ? yield (0, _utils.getFiles)(_this7.path) : [];
      if (!Array.isArray(localHostingFiles)) return localHostingFiles;

      return localHostingFiles ? localHostingFiles.map(function (file) {
        return new HostingFile().loadLocal({ path: file });
      }) : [];
    })();
  }

  listFiles() {
    var _this8 = this;

    return _asyncToGenerator(function* () {
      const remoteFiles = yield _this8.listRemoteFiles();
      const listLocalFiles = yield _this8.listLocalFiles();

      const files = [];
      listLocalFiles.forEach(function (localFile) {
        const file = localFile;
        const remoteCopy = _lodash2.default.find(remoteFiles, { path: _this8.getLocalFilePath(file) });

        if (remoteCopy) {
          file.isUpToDate = file.checksum === remoteCopy.checksum;
          file.isSynced = true;
          _lodash2.default.extend(file, remoteCopy);
        }
        files.push(file);
      });
      return files;
    })();
  }

  getCnameURL() {
    const cname = _lodash2.default.find(this.domains, domain => domain.indexOf('.') !== -1);
    if (cname) {
      return `http://${cname}`;
    }
  }

  // TODO: filtering hostings (wating for core)
  static listRemote(socket) {
    return _asyncToGenerator(function* () {
      debug('listRemote');
      const hostings = yield _session2.default.connection.Hosting.please().list();
      return hostings.map(function (hosting) {
        return new Hosting(hosting.name, socket);
      });
    })();
  }

  getRemote() {
    debug('getRemote()', this);
    return _session2.default.connection.Hosting.please().get({ id: this.name }).then(res => res);
  }
}

exports.default = Hosting;
exports.HostingFile = HostingFile;
//# sourceMappingURL=hosting.js.map