"use strict";

exports.__esModule = true;
exports.sync = sync;
exports.syncAction = syncAction;
exports["default"] = _default;

var _https = require("https");

var _parseJsonObject = _interopRequireDefault(require("parse-json-object"));

var _writeJsonSafe = require("write-json-safe");

var _progress = _interopRequireDefault(require("progress"));

var _load = require("./load");

var _ = require("./");

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

var getOptions = function getOptions(path) {
  if (path === void 0) {
    path = "";
  }

  return {
    hostname: "replicate.npmjs.com",
    port: 443,
    path: path,
    method: "GET"
  };
};

function getEnd() {
  return new Promise(function (resolve) {
    var data = "";
    var options = getOptions();
    (0, _https.request)(options, function (res) {
      res.on("data", function (chunk) {
        data += chunk.toString();
      });
      res.on("end", function () {
        var _ref = (0, _parseJsonObject["default"])(data),
            update_seq = _ref.update_seq;

        resolve(update_seq);
      });
    }).end();
  });
}

var initialState = function initialState(save, end) {
  return {
    data: "",
    start: save.since,
    index: save.since,
    end: end,
    progress: 0,
    elapsed: 0,
    packageNames: save.packageNames.reduce(function (retval, name) {
      retval[name] = true;
      return retval;
    }, {})
  };
};

function isChange(item) {
  return item !== undefined && typeof item.seq === "number" && typeof item.id === "string";
}

function pump(state) {
  while (state.data.indexOf("\n") !== state.data.lastIndexOf("\n")) {
    var newline = state.data.indexOf("\n", 1);
    var line = state.data.slice(0, newline);

    if (line.endsWith(",")) {
      line = line.slice(0, line.length - 1);
    }

    var item = (0, _parseJsonObject["default"])(line);

    if (isChange(item)) {
      state.index = item.seq;
      state.packageNames[item.id] = true;
      state.progress = (state.index - state.start) / (state.end - state.start);
    }

    state.data = state.data.slice(newline);
  }
}

function sync(_temp) {
  var _ref2 = _temp === void 0 ? {} : _temp,
      onData = _ref2.onData,
      onStart = _ref2.onStart,
      onEnd = _ref2.onEnd;

  var startTime = new Date().getTime();
  return new Promise(function (resolve) {
    (0, _load.load)().then(function (save) {
      getEnd().then(function (end) {
        var state = initialState(save, end);

        if (onStart) {
          onStart(state);
        }

        (0, _https.request)(getOptions("/_changes?since=" + save.since), function (res) {
          res.on("data", function (chunk) {
            state.data += chunk.toString();
            pump(state);
            state.elapsed = new Date().getTime() - startTime;

            if (onData) {
              onData(state);
            }
          });
          res.on("end", function () {
            if (onEnd) {
              onEnd(state);
            }

            var save = {
              since: state.index,
              packageNames: Object.keys(state.packageNames)
            };
            (0, _writeJsonSafe.writeJSON)(_.savePath, save, false).then(function () {
              resolve(save);
            });
          });
        }).end();
      });
    });
  });
}

function syncAction() {
  var bar;
  sync({
    onStart: function onStart(state) {
      bar = new _progress["default"]("syncing [:bar] :percent ", {
        total: state.end - state.start
      });
    },
    onData: function onData(state) {
      bar.update(state.progress);
    },
    onEnd: function onEnd(state) {
      console.log("Packages: " + Object.keys(state.packageNames).length);
      console.log("Time: " + state.elapsed / 1000 + "s");
    }
  });
}

function _default(program) {
  program.command("sync").description("Sync latest packages from NPM").action(syncAction);
}