"use strict";

var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");

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

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.componentToPrintableForDiff = componentToPrintableForDiff;
exports.prettifyFieldName = prettifyFieldName;
exports.getDiffBetweenObjects = getDiffBetweenObjects;
exports.diffBetweenComponentsObjects = diffBetweenComponentsObjects;

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

  _ramda = function () {
    return data;
  };

  return data;
}

function RA() {
  const data = _interopRequireWildcard(require("ramda-adjunct"));

  RA = function () {
    return data;
  };

  return data;
}

function _chalk() {
  const data = _interopRequireDefault(require("chalk"));

  _chalk = function () {
    return data;
  };

  return data;
}

function _objectDiff() {
  const data = _interopRequireDefault(require("object-diff"));

  _objectDiff = function () {
    return data;
  };

  return data;
}

function _normalizePath() {
  const data = _interopRequireDefault(require("normalize-path"));

  _normalizePath = function () {
    return data;
  };

  return data;
}

function _arrayDifference() {
  const data = _interopRequireDefault(require("array-difference"));

  _arrayDifference = function () {
    return data;
  };

  return data;
}

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

  _consumerComponent = function () {
    return data;
  };

  return data;
}

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

  _ = function () {
    return data;
  };

  return data;
}

function _envExtension() {
  const data = _interopRequireDefault(require("../../extensions/env-extension"));

  _envExtension = function () {
    return data;
  };

  return data;
}

function componentToPrintableForDiff(component) {
  const obj = {};

  const parsePackages = packages => {
    return !_ramda().default.isEmpty(packages) && !_ramda().default.isNil(packages) ? Object.keys(packages).map(key => `${key}@${packages[key]}`) : null;
  };

  const parseEnvFiles = envExtension => {
    // $FlowFixMe sadly, Flow doesn't know what isNilOrEmpty does
    if (RA().isNilOrEmpty(envExtension) || RA().isNilOrEmpty(envExtension.files)) return null; // $FlowFixMe sadly, Flow doesn't know what isNilOrEmpty does

    return envExtension.files.map(file => `${file.name} => ${file.relative}`).sort();
  };

  const {
    lang,
    compiler,
    tester,
    dependencies,
    devDependencies,
    compilerDependencies,
    testerDependencies,
    packageDependencies,
    devPackageDependencies,
    compilerPackageDependencies,
    testerPackageDependencies,
    files,
    mainFile,
    deprecated
  } = component;
  const parsedDevPackageDependencies = parsePackages(devPackageDependencies) || [];
  const parsedCompilerPackageDependencies = parsePackages(compilerPackageDependencies) || [];
  const parsedTesterPackageDependencies = parsePackages(testerPackageDependencies) || [];
  const printableDevPackageDependencies = [...parsedDevPackageDependencies, ...parsedCompilerPackageDependencies, ...parsedTesterPackageDependencies];
  const printableCompilerDependencies = compilerDependencies.toStringOfIds();
  const printableTesterDependencies = testerDependencies.toStringOfIds();
  const peerPackageDependencies = [].concat(parsePackages(component.peerPackageDependencies)).filter(x => x);
  const overrides = component.overrides.componentOverridesData;
  obj.id = component.id.toStringWithoutScope();
  obj.compiler = compiler ? compiler.name : null;
  obj.compilerFiles = parseEnvFiles(compiler);
  obj.language = lang || null;
  obj.tester = tester ? tester.name : null;
  obj.testerFiles = parseEnvFiles(tester);
  obj.mainFile = mainFile ? (0, _normalizePath().default)(mainFile) : null;
  obj.dependencies = dependencies.toStringOfIds().sort().concat(parsePackages(packageDependencies)).filter(x => x);
  obj.devDependencies = devDependencies.toStringOfIds().sort().concat(printableDevPackageDependencies).concat(printableCompilerDependencies).concat(printableTesterDependencies).filter(x => x);
  obj.peerDependencies = peerPackageDependencies.length ? peerPackageDependencies : undefined;
  obj.files = files && !_ramda().default.isEmpty(files) && !_ramda().default.isNil(files) ? files.filter(file => !file.test).map(file => (0, _normalizePath().default)(file.relative)) : null;
  obj.specs = files && !_ramda().default.isEmpty(files) && !_ramda().default.isNil(files) && _ramda().default.find(_ramda().default.propEq('test', true))(files) ? files.filter(file => file.test).map(file => (0, _normalizePath().default)(file.relative)) : null;
  obj.deprecated = deprecated ? 'True' : null;
  obj.overridesDependencies = parsePackages(overrides.dependencies);
  obj.overridesDevDependencies = parsePackages(overrides.devDependencies);
  obj.overridesPeerDependencies = parsePackages(overrides.peerDependencies);
  return obj;
}

function prettifyFieldName(field) {
  return `${field[0].toUpperCase()}${field.substr(1)}`.replace(/([A-Z])/g, ' $1').trim();
}

function comparator(a, b) {
  if (a instanceof Array && b instanceof Array) {
    return _ramda().default.isEmpty((0, _arrayDifference().default)(a, b));
  }

  return a === b;
}

function getDiffBetweenObjects(objectLeft, objectRight) {
  return _objectDiff().default.custom({
    equal: comparator
  }, objectLeft, objectRight);
}

function diffBetweenComponentsObjects(consumer, componentLeft, componentRight, verbose) {
  const printableLeft = componentToPrintableForDiff(componentLeft);
  const printableRight = componentToPrintableForDiff(componentRight);
  const fieldsDiff = getDiffBetweenObjects(printableLeft, printableRight);

  if (!componentLeft.version || !componentRight.version) {
    throw new Error('diffBetweenComponentsObjects component does not have a version');
  }

  const areVersionsTheSame = componentLeft.version === componentRight.version;
  const labelLeft = areVersionsTheSame ? `${componentLeft.version} original` : componentLeft.version;
  const labelRight = areVersionsTheSame ? `${componentRight.version} modified` : componentRight.version;

  const titleLeft = field => `--- ${prettifyFieldName(field)} (${labelLeft})\n`;

  const titleRight = field => `+++ ${prettifyFieldName(field)} (${labelRight})\n`;

  const printFieldValue = fieldValue => {
    if (typeof fieldValue === 'string') return fieldValue;
    if (Array.isArray(fieldValue)) return `[ ${fieldValue.join(', ')} ]`;
    throw new Error(`diffBetweenComponentsObjects: not support ${typeof fieldValue}`);
  };

  const printFieldLeft = field => {
    const fieldValue = printableLeft[field];
    if (!fieldValue) return '';
    return `- ${printFieldValue(fieldValue)}\n`;
  };

  const printFieldRight = field => {
    const fieldValue = printableRight[field];
    if (!fieldValue) return '';
    return `+ ${printFieldValue(fieldValue)}\n`;
  };

  const fieldsDiffOutput = Object.keys(fieldsDiff).map(field => {
    const title = titleLeft(field) + _chalk().default.bold(titleRight(field));

    const value = _chalk().default.red(printFieldLeft(field)) + _chalk().default.green(printFieldRight(field));

    const diffOutput = title + value;
    return {
      fieldName: field,
      diffOutput
    };
  });

  const dependenciesOutput = () => {
    if (!verbose) return [];
    const dependenciesLeft = componentLeft.getAllDependencies();
    const dependenciesRight = componentRight.getAllDependencies();
    if (_ramda().default.isEmpty(dependenciesLeft) || _ramda().default.isEmpty(dependenciesRight)) return [];
    return dependenciesLeft.reduce((acc, dependencyLeft) => {
      const idStr = dependencyLeft.id.toString();
      const dependencyRight = dependenciesRight.find(dep => dep.id.isEqual(dependencyLeft.id));
      if (!dependencyRight) return acc;
      if (JSON.stringify(dependencyLeft.relativePaths) === JSON.stringify(dependencyRight.relativePaths)) return acc;
      const fieldName = `Dependency ${idStr} relative-paths`;

      const title = titleLeft(fieldName) + _chalk().default.bold(titleRight(fieldName));

      const getValue = (fieldValue, left) => {
        if (_ramda().default.isEmpty(fieldValue)) return '';
        const sign = left ? '-' : '+';
        const jsonOutput = JSON.stringify(fieldValue, null, `${sign} `);
        return `${jsonOutput}\n`;
      };

      const value = _chalk().default.red(getValue(dependencyLeft.relativePaths, true)) + _chalk().default.green(getValue(dependencyRight.relativePaths, false));

      const diffOutput = title + value;
      acc.push({
        fieldName,
        diffOutput
      });
      return acc;
    }, []);
  };

  const envs = ['compiler', 'tester'];
  const fieldsEnvsConfigOutput = envs.map(env => {
    // $FlowFixMe
    const leftConfig = componentLeft[env] && componentLeft[env].dynamicConfig ? componentLeft[env].dynamicConfig : {};
    const rightConfig = // $FlowFixMe
    componentRight[env] && componentRight[env].dynamicConfig ? componentRight[env].dynamicConfig : {}; // $FlowFixMe we remove the null later

    if (JSON.stringify(leftConfig) === JSON.stringify(rightConfig)) return null;
    const fieldName = `${env} configuration`;

    const title = titleLeft(fieldName) + _chalk().default.bold(titleRight(fieldName));

    const getValue = (fieldValue, left) => {
      if (_ramda().default.isEmpty(fieldValue)) return '';
      const sign = left ? '-' : '+';
      const jsonOutput = JSON.stringify(fieldValue, null, `${sign} `);
      return `${jsonOutput}\n`;
    };

    const value = _chalk().default.red(getValue(leftConfig, true)) + _chalk().default.green(getValue(rightConfig, false));

    const diffOutput = title + value;
    return {
      fieldName,
      diffOutput
    };
  }).filter(x => x);
  const allDiffs = [...fieldsDiffOutput, ...fieldsEnvsConfigOutput, ...dependenciesOutput()];
  return _ramda().default.isEmpty(allDiffs) ? undefined : allDiffs;
}