"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

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

function _bluebird() {
  const data = require("bluebird");

  _bluebird = function () {
    return data;
  };

  return data;
}

function _ramda() {
  const data = _interopRequireDefault(require("ramda"));

  _ramda = function () {
    return data;
  };

  return data;
}

function _() {
  const data = require("..");

  _ = function () {
    return data;
  };

  return data;
}

function _bitId() {
  const data = require("../../bit-id");

  _bitId = function () {
    return data;
  };

  return data;
}

function _generalError() {
  const data = _interopRequireDefault(require("../../error/general-error"));

  _generalError = function () {
    return data;
  };

  return data;
}

function _consumerComponent() {
  const data = _interopRequireDefault(require("../component/consumer-component"));

  _consumerComponent = function () {
    return data;
  };

  return data;
}

function _sources() {
  const data = require("../component/sources");

  _sources = function () {
    return data;
  };

  return data;
}

function _repositories() {
  const data = require("../../scope/repositories");

  _repositories = function () {
    return data;
  };

  return data;
}

function _diffFiles() {
  const data = _interopRequireDefault(require("../../utils/diff-files"));

  _diffFiles = function () {
    return data;
  };

  return data;
}

function _models() {
  const data = require("../../scope/models");

  _models = function () {
    return data;
  };

  return data;
}

function _componentsObjectDiff() {
  const data = require("./components-object-diff");

  _componentsObjectDiff = function () {
    return data;
  };

  return data;
}

var _default =
/*#__PURE__*/
function () {
  var _componentsDiff = (0, _bluebird().coroutine)(function* (consumer, ids, version, toVersion, verbose) {
    const {
      components
    } = yield consumer.loadComponents(ids);
    if (!components) throw new (_generalError().default)('failed loading the components');
    const tmp = new (_repositories().Tmp)(consumer.scope); // try to resolve ids scope of by components array

    const idsWithScope = ids.map(id => {
      if (!id.scope && components) {
        const foundComponent = components.find(o => o.name === id.name);
        if (foundComponent) return id.changeScope(foundComponent.scope);
      }

      return id;
    });

    try {
      const getResults = () => {
        if (version && toVersion) {
          return Promise.all(idsWithScope.map(id => getComponentDiffBetweenVersions(id)));
        }

        if (version) {
          return Promise.all(components.map(component => getComponentDiffOfVersion(component)));
        }

        return Promise.all(components.map(component => getComponentDiff(component)));
      };

      const componentsDiffResults = yield getResults();
      yield tmp.clear();
      return componentsDiffResults;
    } catch (err) {
      yield tmp.clear();
      throw err;
    }

    function getComponentDiffOfVersion(_x6) {
      return _getComponentDiffOfVersion.apply(this, arguments);
    }

    function _getComponentDiffOfVersion() {
      _getComponentDiffOfVersion = (0, _bluebird().coroutine)(function* (component) {
        const diffResult = {
          id: component.id,
          hasDiff: false
        };
        const modelComponent = yield consumer.scope.getModelComponentIfExist(component.id);

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

        const repository = consumer.scope.objects;
        const fromVersionObject = yield modelComponent.loadVersion(version, repository);
        const versionFiles = yield fromVersionObject.modelFilesToSourceFiles(repository);
        const fsFiles = component.files; // $FlowFixMe version must be defined as the component.componentFromModel do exist

        const versionB = component.id.version; // $FlowFixMe this function gets called only when version is set

        diffResult.filesDiff = yield getFilesDiff(tmp, versionFiles, fsFiles, version, versionB);
        const fromVersionComponent = yield modelComponent.toConsumerComponent(version, consumer.scope.name, repository);
        yield updateFieldsAndEnvsDiff(fromVersionComponent, component, diffResult);
        return diffResult;
      });
      return _getComponentDiffOfVersion.apply(this, arguments);
    }

    function getComponentDiffBetweenVersions(_x7) {
      return _getComponentDiffBetweenVersions.apply(this, arguments);
    }

    function _getComponentDiffBetweenVersions() {
      _getComponentDiffBetweenVersions = (0, _bluebird().coroutine)(function* (id) {
        const diffResult = {
          id,
          hasDiff: false
        };
        const modelComponent = yield consumer.scope.getModelComponentIfExist(id);

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

        const repository = consumer.scope.objects;
        const fromVersionObject = yield modelComponent.loadVersion(version, repository);
        const toVersionObject = yield modelComponent.loadVersion(toVersion, repository);
        const fromVersionFiles = yield fromVersionObject.modelFilesToSourceFiles(repository);
        const toVersionFiles = yield toVersionObject.modelFilesToSourceFiles(repository); // $FlowFixMe version and toVersion are set when calling this function

        diffResult.filesDiff = yield getFilesDiff(tmp, fromVersionFiles, toVersionFiles, version, toVersion);
        const fromVersionComponent = yield modelComponent.toConsumerComponent(version, consumer.scope.name, repository);
        const toVersionComponent = yield modelComponent.toConsumerComponent(toVersion, consumer.scope.name, repository);
        yield updateFieldsAndEnvsDiff(fromVersionComponent, toVersionComponent, diffResult);
        return diffResult;
      });
      return _getComponentDiffBetweenVersions.apply(this, arguments);
    }

    function getComponentDiff(_x8) {
      return _getComponentDiff.apply(this, arguments);
    }

    function _getComponentDiff() {
      _getComponentDiff = (0, _bluebird().coroutine)(function* (component) {
        const diffResult = {
          id: component.id,
          hasDiff: false
        };

        if (!component.componentFromModel) {
          // it's a new component. not modified. nothing to check.
          return diffResult;
        }

        const modelFiles = component.componentFromModel.files;
        const fsFiles = component.files; // $FlowFixMe version must be defined as the component.componentFromModel do exist

        diffResult.filesDiff = yield getFilesDiff(tmp, modelFiles, fsFiles, component.id.version, component.id.version); // $FlowFixMe we made sure already that component.componentFromModel is defined

        yield updateFieldsAndEnvsDiff(component.componentFromModel, component, diffResult);
        return diffResult;
      });
      return _getComponentDiff.apply(this, arguments);
    }

    function updateFieldsAndEnvsDiff(_x9, _x10, _x11) {
      return _updateFieldsAndEnvsDiff.apply(this, arguments);
    }

    function _updateFieldsAndEnvsDiff() {
      _updateFieldsAndEnvsDiff = (0, _bluebird().coroutine)(function* (componentA, componentB, diffResult) {
        const envsFilesDiff = yield getEnvFilesDiff(tmp, consumer, componentA, componentB); // $FlowFixMe diffResult.filesDiff is populated at this point

        diffResult.filesDiff = diffResult.filesDiff.concat(envsFilesDiff);
        diffResult.fieldsDiff = (0, _componentsObjectDiff().diffBetweenComponentsObjects)(consumer, componentA, componentB, verbose);
        diffResult.hasDiff = hasDiff(diffResult);
      });
      return _updateFieldsAndEnvsDiff.apply(this, arguments);
    }
  });

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

  return componentsDiff;
}();

exports.default = _default;

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

function getOneFileDiff(_x12, _x13, _x14, _x15, _x16) {
  return _getOneFileDiff.apply(this, arguments);
}

function _getOneFileDiff() {
  _getOneFileDiff = (0, _bluebird().coroutine)(function* (filePathA, filePathB, fileALabel, fileBLabel, fileName) {
    const fileDiff = yield (0, _diffFiles().default)(filePathA, filePathB);
    if (!fileDiff) return '';
    const diffStartsString = '--- '; // the part before this string is not needed for our purpose

    const diffStart = fileDiff.indexOf(diffStartsString);
    if (!diffStart || diffStart < 1) return ''; // invalid diff
    // e.g. Linux: --- a/private/var/folders/z ... .js
    // Windows: --- "a/C:\\Users\\David\\AppData\\Local\\Temp\\bit ... .js

    const regExpA = /--- ["]?a.*\n/; // exact "---", follow by a or "a (for Windows) then \n

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

    return fileDiff.substr(diffStart).replace(regExpA, `--- ${fileName} (${fileALabel})\n`).replace(regExpB, `+++ ${fileName} (${fileBLabel})\n`);
  });
  return _getOneFileDiff.apply(this, arguments);
}

function getEnvFilesDiff(_x17, _x18, _x19, _x20) {
  return _getEnvFilesDiff.apply(this, arguments);
}

function _getEnvFilesDiff() {
  _getEnvFilesDiff = (0, _bluebird().coroutine)(function* (tmp, consumer, componentA, componentB) {
    const envs = ['compiler', 'tester'];
    const envsDiffP = envs.map(env => {
      // $FlowFixMe
      const filesA = componentA[env] && componentA[env].files ? componentA[env].files : []; // $FlowFixMe

      const filesB = componentB[env] && componentB[env].files ? componentB[env].files : [];
      const filesAVersion = componentA.version;
      const filesBVersion = componentB.version;

      if (!filesAVersion || !filesBVersion) {
        throw new Error('diffBetweenComponentsObjects component does not have a version');
      }

      return getFilesDiff(tmp, filesA, filesB, filesAVersion, filesBVersion, 'name');
    });
    const envsDiff = yield Promise.all(envsDiffP);
    return _ramda().default.flatten(envsDiff);
  });
  return _getEnvFilesDiff.apply(this, arguments);
}

function getFilesDiff(_x21, _x22, _x23, _x24, _x25) {
  return _getFilesDiff.apply(this, arguments);
}

function _getFilesDiff() {
  _getFilesDiff = (0, _bluebird().coroutine)(function* (tmp, filesA, filesB, filesAVersion, filesBVersion, fileNameAttribute = 'relative') {
    const filesAPaths = filesA.map(f => f[fileNameAttribute]);
    const filesBPaths = filesB.map(f => f[fileNameAttribute]);

    const allPaths = _ramda().default.uniq(filesAPaths.concat(filesBPaths));

    const fileALabel = filesAVersion === filesBVersion ? `${filesAVersion} original` : filesAVersion;
    const fileBLabel = filesAVersion === filesBVersion ? `${filesBVersion} modified` : filesBVersion;
    const filesDiffP = allPaths.map(
    /*#__PURE__*/
    function () {
      var _ref = (0, _bluebird().coroutine)(function* (relativePath) {
        const getFilePath =
        /*#__PURE__*/
        function () {
          var _ref2 = (0, _bluebird().coroutine)(function* (files) {
            const file = files.find(f => f[fileNameAttribute] === relativePath);
            const fileContent = file ? file.contents : '';
            return tmp.save(fileContent);
          });

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

        const [fileAPath, fileBPath] = yield Promise.all([getFilePath(filesA), getFilePath(filesB)]);
        const diffOutput = yield getOneFileDiff(fileAPath, fileBPath, fileALabel, fileBLabel, relativePath);
        return {
          filePath: relativePath,
          diffOutput
        };
      });

      return function (_x26) {
        return _ref.apply(this, arguments);
      };
    }());
    return Promise.all(filesDiffP);
  });
  return _getFilesDiff.apply(this, arguments);
}