'use strict';

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

var _regenerator;

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

var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();

var getComponentDiffOfVersion = function () {
  var _ref3 = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee2(consumer, tmp, component, version) {
    var diffResult, modelComponent, repository, fromVersionObject, versionFiles, fsFiles, versionB, fromVersionComponent;
    return (_regenerator || _load_regenerator()).default.wrap(function _callee2$(_context2) {
      while (1) {
        switch (_context2.prev = _context2.next) {
          case 0:
            diffResult = { id: component.id, hasDiff: false };
            _context2.next = 3;
            return consumer.scope.getModelComponentIfExist(component.id);

          case 3:
            modelComponent = _context2.sent;

            if (modelComponent) {
              _context2.next = 6;
              break;
            }

            throw new (_generalError || _load_generalError()).default(`component ${component.id.toString()} doesn't have any version yet`);

          case 6:
            repository = consumer.scope.objects;
            _context2.next = 9;
            return modelComponent.loadVersion(version, repository);

          case 9:
            fromVersionObject = _context2.sent;
            _context2.next = 12;
            return fromVersionObject.modelFilesToSourceFiles(repository);

          case 12:
            versionFiles = _context2.sent;
            fsFiles = component.cloneFilesWithSharedDir();
            // $FlowFixMe version must be defined as the component.componentFromModel do exist

            versionB = component.id.version;
            _context2.next = 17;
            return getFilesDiff(tmp, versionFiles, fsFiles, version, versionB);

          case 17:
            diffResult.filesDiff = _context2.sent;
            _context2.next = 20;
            return modelComponent.toConsumerComponent(version, consumer.scope.name, repository);

          case 20:
            fromVersionComponent = _context2.sent;
            _context2.next = 23;
            return updateFieldsAndEnvsDiff(tmp, consumer, fromVersionComponent, component, diffResult);

          case 23:
            return _context2.abrupt('return', diffResult);

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

  return function getComponentDiffOfVersion(_x5, _x6, _x7, _x8) {
    return _ref3.apply(this, arguments);
  };
}();

var getComponentDiffBetweenVersions = function () {
  var _ref4 = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee3(consumer, tmp, id, version, toVersion) {
    var diffResult, modelComponent, repository, fromVersionObject, toVersionObject, fromVersionFiles, toVersionFiles, fromVersionComponent, toVersionComponent;
    return (_regenerator || _load_regenerator()).default.wrap(function _callee3$(_context3) {
      while (1) {
        switch (_context3.prev = _context3.next) {
          case 0:
            diffResult = { id, hasDiff: false };
            _context3.next = 3;
            return consumer.scope.getModelComponentIfExist(id);

          case 3:
            modelComponent = _context3.sent;

            if (modelComponent) {
              _context3.next = 6;
              break;
            }

            throw new (_generalError || _load_generalError()).default(`component ${id.toString()} doesn't have any version yet`);

          case 6:
            repository = consumer.scope.objects;
            _context3.next = 9;
            return modelComponent.loadVersion(version, repository);

          case 9:
            fromVersionObject = _context3.sent;
            _context3.next = 12;
            return modelComponent.loadVersion(toVersion, repository);

          case 12:
            toVersionObject = _context3.sent;
            _context3.next = 15;
            return fromVersionObject.modelFilesToSourceFiles(repository);

          case 15:
            fromVersionFiles = _context3.sent;
            _context3.next = 18;
            return toVersionObject.modelFilesToSourceFiles(repository);

          case 18:
            toVersionFiles = _context3.sent;
            _context3.next = 21;
            return getFilesDiff(tmp, fromVersionFiles, toVersionFiles, version, toVersion);

          case 21:
            diffResult.filesDiff = _context3.sent;
            _context3.next = 24;
            return modelComponent.toConsumerComponent(version, consumer.scope.name, repository);

          case 24:
            fromVersionComponent = _context3.sent;
            _context3.next = 27;
            return modelComponent.toConsumerComponent(toVersion, consumer.scope.name, repository);

          case 27:
            toVersionComponent = _context3.sent;
            _context3.next = 30;
            return updateFieldsAndEnvsDiff(tmp, consumer, fromVersionComponent, toVersionComponent, diffResult);

          case 30:
            return _context3.abrupt('return', diffResult);

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

  return function getComponentDiffBetweenVersions(_x9, _x10, _x11, _x12, _x13) {
    return _ref4.apply(this, arguments);
  };
}();

var getComponentDiff = function () {
  var _ref5 = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee4(consumer, tmp, component) {
    var diffResult, version, modelFiles, fsFiles;
    return (_regenerator || _load_regenerator()).default.wrap(function _callee4$(_context4) {
      while (1) {
        switch (_context4.prev = _context4.next) {
          case 0:
            diffResult = { id: component.id, hasDiff: false };

            if (component.componentFromModel) {
              _context4.next = 3;
              break;
            }

            return _context4.abrupt('return', diffResult);

          case 3:
            // $FlowFixMe version must be defined as the component.componentFromModel do exist
            version = component.id.version;
            modelFiles = component.componentFromModel.files;
            fsFiles = component.cloneFilesWithSharedDir();
            _context4.next = 8;
            return getFilesDiff(tmp, modelFiles, fsFiles, version, version);

          case 8:
            diffResult.filesDiff = _context4.sent;
            _context4.next = 11;
            return updateFieldsAndEnvsDiff(tmp, consumer, component.componentFromModel, component, diffResult);

          case 11:
            return _context4.abrupt('return', diffResult);

          case 12:
          case 'end':
            return _context4.stop();
        }
      }
    }, _callee4, this);
  }));

  return function getComponentDiff(_x14, _x15, _x16) {
    return _ref5.apply(this, arguments);
  };
}();

var updateFieldsAndEnvsDiff = function () {
  var _ref6 = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee5(tmp, consumer, componentA, componentB, diffResult) {
    var envsFilesDiff;
    return (_regenerator || _load_regenerator()).default.wrap(function _callee5$(_context5) {
      while (1) {
        switch (_context5.prev = _context5.next) {
          case 0:
            _context5.next = 2;
            return getEnvFilesDiff(tmp, consumer, componentA, componentB);

          case 2:
            envsFilesDiff = _context5.sent;

            // $FlowFixMe diffResult.filesDiff is populated at this point
            diffResult.filesDiff = diffResult.filesDiff.concat(envsFilesDiff);
            diffResult.fieldsDiff = (0, (_componentsObjectDiff || _load_componentsObjectDiff()).diffBetweenComponentsObjects)(consumer, componentA, componentB);
            diffResult.hasDiff = hasDiff(diffResult);

          case 6:
          case 'end':
            return _context5.stop();
        }
      }
    }, _callee5, this);
  }));

  return function updateFieldsAndEnvsDiff(_x17, _x18, _x19, _x20, _x21) {
    return _ref6.apply(this, arguments);
  };
}();

var getOneFileDiff = function () {
  var _ref7 = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee6(filePathA, filePathB, fileALabel, fileBLabel, fileName) {
    var fileDiff, diffStartsString, diffStart, regExpA, regExpB;
    return (_regenerator || _load_regenerator()).default.wrap(function _callee6$(_context6) {
      while (1) {
        switch (_context6.prev = _context6.next) {
          case 0:
            _context6.next = 2;
            return (0, (_diffFiles || _load_diffFiles()).default)(filePathA, filePathB);

          case 2:
            fileDiff = _context6.sent;

            if (fileDiff) {
              _context6.next = 5;
              break;
            }

            return _context6.abrupt('return', '');

          case 5:
            diffStartsString = '--- '; // the part before this string is not needed for our purpose

            diffStart = fileDiff.indexOf(diffStartsString);

            if (!(!diffStart || diffStart < 1)) {
              _context6.next = 9;
              break;
            }

            return _context6.abrupt('return', '');

          case 9:
            // invalid diff

            // e.g. Linux: --- a/private/var/folders/z ... .js
            // Windows: --- "a/C:\\Users\\David\\AppData\\Local\\Temp\\bit ... .js
            regExpA = /--- ["]?a.*\n/; // exact "---", follow by a or "a (for Windows) then \n

            regExpB = /\+\+\+ ["]?b.*\n/; // exact "+++", follow by b or "b (for Windows) then \n

            return _context6.abrupt('return', fileDiff.substr(diffStart).replace(regExpA, `--- ${fileName} (${fileALabel})\n`).replace(regExpB, `+++ ${fileName} (${fileBLabel})\n`));

          case 12:
          case 'end':
            return _context6.stop();
        }
      }
    }, _callee6, this);
  }));

  return function getOneFileDiff(_x22, _x23, _x24, _x25, _x26) {
    return _ref7.apply(this, arguments);
  };
}();

var getEnvFilesDiff = function () {
  var _ref8 = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee7(tmp, consumer, componentA, componentB) {
    var envs, envsDiffP, envsDiff;
    return (_regenerator || _load_regenerator()).default.wrap(function _callee7$(_context7) {
      while (1) {
        switch (_context7.prev = _context7.next) {
          case 0:
            envs = ['compiler', 'tester'];
            envsDiffP = envs.map(function (env) {
              // $FlowFixMe
              var filesA = componentA[env] && componentA[env].files ? componentA[env].files : [];
              // $FlowFixMe
              var filesB = componentB[env] && componentB[env].files ? componentB[env].files : [];
              var filesAVersion = componentA.version;
              var filesBVersion = componentB.version;
              if (!filesAVersion || !filesBVersion) {
                throw new Error('diffBetweenComponentsObjects component does not have a version');
              }
              return getFilesDiff(tmp, filesA, filesB, filesAVersion, filesBVersion, 'name');
            });
            _context7.next = 4;
            return Promise.all(envsDiffP);

          case 4:
            envsDiff = _context7.sent;
            return _context7.abrupt('return', (_ramda || _load_ramda()).default.flatten(envsDiff));

          case 6:
          case 'end':
            return _context7.stop();
        }
      }
    }, _callee7, this);
  }));

  return function getEnvFilesDiff(_x27, _x28, _x29, _x30) {
    return _ref8.apply(this, arguments);
  };
}();

var getFilesDiff = function () {
  var _ref9 = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee10(tmp, filesA, filesB, filesAVersion, filesBVersion) {
    var _this = this;

    var fileNameAttribute = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 'relative';
    var filesAPaths, filesBPaths, allPaths, fileALabel, fileBLabel, filesDiffP;
    return (_regenerator || _load_regenerator()).default.wrap(function _callee10$(_context10) {
      while (1) {
        switch (_context10.prev = _context10.next) {
          case 0:
            filesAPaths = filesA.map(function (f) {
              return f[fileNameAttribute];
            });
            filesBPaths = filesB.map(function (f) {
              return f[fileNameAttribute];
            });
            allPaths = (_ramda || _load_ramda()).default.uniq(filesAPaths.concat(filesBPaths));
            fileALabel = filesAVersion === filesBVersion ? `${filesAVersion} original` : filesAVersion;
            fileBLabel = filesAVersion === filesBVersion ? `${filesBVersion} modified` : filesBVersion;
            filesDiffP = allPaths.map(function () {
              var _ref10 = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee9(relativePath) {
                var getFilePath, _ref12, _ref13, fileAPath, fileBPath, diffOutput;

                return (_regenerator || _load_regenerator()).default.wrap(function _callee9$(_context9) {
                  while (1) {
                    switch (_context9.prev = _context9.next) {
                      case 0:
                        getFilePath = function () {
                          var _ref11 = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee8(files) {
                            var file, fileContent;
                            return (_regenerator || _load_regenerator()).default.wrap(function _callee8$(_context8) {
                              while (1) {
                                switch (_context8.prev = _context8.next) {
                                  case 0:
                                    file = files.find(function (f) {
                                      return f[fileNameAttribute] === relativePath;
                                    });
                                    fileContent = file ? file.contents : '';
                                    return _context8.abrupt('return', tmp.save(fileContent));

                                  case 3:
                                  case 'end':
                                    return _context8.stop();
                                }
                              }
                            }, _callee8, _this);
                          }));

                          return function getFilePath(_x38) {
                            return _ref11.apply(this, arguments);
                          };
                        }();

                        _context9.next = 3;
                        return Promise.all([getFilePath(filesA), getFilePath(filesB)]);

                      case 3:
                        _ref12 = _context9.sent;
                        _ref13 = _slicedToArray(_ref12, 2);
                        fileAPath = _ref13[0];
                        fileBPath = _ref13[1];
                        _context9.next = 9;
                        return getOneFileDiff(fileAPath, fileBPath, fileALabel, fileBLabel, relativePath);

                      case 9:
                        diffOutput = _context9.sent;
                        return _context9.abrupt('return', { filePath: relativePath, diffOutput });

                      case 11:
                      case 'end':
                        return _context9.stop();
                    }
                  }
                }, _callee9, _this);
              }));

              return function (_x37) {
                return _ref10.apply(this, arguments);
              };
            }());
            return _context10.abrupt('return', Promise.all(filesDiffP));

          case 7:
          case 'end':
            return _context10.stop();
        }
      }
    }, _callee10, this);
  }));

  return function getFilesDiff(_x32, _x33, _x34, _x35, _x36) {
    return _ref9.apply(this, arguments);
  };
}();

var _ramda;

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

var _;

function _load_() {
  return _ = require('..');
}

var _bitId;

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

var _generalError;

function _load_generalError() {
  return _generalError = _interopRequireDefault(require('../../error/general-error'));
}

var _consumerComponent;

function _load_consumerComponent() {
  return _consumerComponent = _interopRequireDefault(require('../component/consumer-component'));
}

var _sources;

function _load_sources() {
  return _sources = require('../component/sources');
}

var _repositories;

function _load_repositories() {
  return _repositories = require('../../scope/repositories');
}

var _diffFiles;

function _load_diffFiles() {
  return _diffFiles = _interopRequireDefault(require('../../utils/diff-files'));
}

var _models;

function _load_models() {
  return _models = require('../../scope/models');
}

var _componentsObjectDiff;

function _load_componentsObjectDiff() {
  return _componentsObjectDiff = require('./components-object-diff');
}

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

exports.default = function () {
  var _ref = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee(consumer, ids, version, toVersion) {
    var _ref2, components, tmp, getResults, componentsDiffResults;

    return (_regenerator || _load_regenerator()).default.wrap(function _callee$(_context) {
      while (1) {
        switch (_context.prev = _context.next) {
          case 0:
            _context.next = 2;
            return consumer.loadComponents(ids);

          case 2:
            _ref2 = _context.sent;
            components = _ref2.components;

            if (components) {
              _context.next = 6;
              break;
            }

            throw new (_generalError || _load_generalError()).default('failed loading the components');

          case 6:
            tmp = new (_repositories || _load_repositories()).Tmp(consumer.scope);

            // try to resolve ids scope of by components array

            ids.forEach(function (id) {
              if (!id.scope && components) {
                var foundComponent = components.find(function (o) {
                  return o.box === id.box && o.name === id.name;
                });
                if (foundComponent) id.scope = foundComponent.scope;
              }
            });

            _context.prev = 8;

            getResults = function getResults() {
              if (version && toVersion) {
                // $FlowFixMe - version, toVersion are string here, the error is unclear
                return Promise.all(ids.map(function (id) {
                  return getComponentDiffBetweenVersions(consumer, tmp, id, version, toVersion);
                }));
              }
              if (version) {
                // $FlowFixMe - version is string here, the error is unclear
                return Promise.all(components.map(function (component) {
                  return getComponentDiffOfVersion(consumer, tmp, component, version);
                }));
              }
              return Promise.all(components.map(function (component) {
                return getComponentDiff(consumer, tmp, component);
              }));
            };

            _context.next = 12;
            return getResults();

          case 12:
            componentsDiffResults = _context.sent;
            _context.next = 15;
            return tmp.clear();

          case 15:
            return _context.abrupt('return', componentsDiffResults);

          case 18:
            _context.prev = 18;
            _context.t0 = _context['catch'](8);
            _context.next = 22;
            return tmp.clear();

          case 22:
            throw _context.t0;

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

  function componentsDiff(_x, _x2, _x3, _x4) {
    return _ref.apply(this, arguments);
  }

  return componentsDiff;
}();

function hasDiff(diffResult) {
  return !!(diffResult.filesDiff && diffResult.filesDiff.find(function (file) {
    return file.diffOutput;
  }) || diffResult.fieldsDiff);
}