"use strict";

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

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

function _objectSpread2() {
  const data = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread"));

  _objectSpread2 = function () {
    return data;
  };

  return data;
}

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

  _ramda = function () {
    return data;
  };

  return data;
}

function _objects() {
  const data = require("../objects");

  _objects = function () {
    return data;
  };

  return data;
}

function _source() {
  const data = _interopRequireDefault(require("./source"));

  _source = function () {
    return data;
  };

  return data;
}

function _utils() {
  const data = require("../../utils");

  _utils = function () {
    return data;
  };

  return data;
}

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

  _component = function () {
    return data;
  };

  return data;
}

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

  _bitId = function () {
    return data;
  };

  return data;
}

function _constants() {
  const data = require("../../constants");

  _constants = function () {
    return data;
  };

  return data;
}

function _dependencies() {
  const data = require("../../consumer/component/dependencies");

  _dependencies = function () {
    return data;
  };

  return data;
}

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

  _extensionFile = function () {
    return data;
  };

  return data;
}

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

  _sources = function () {
    return data;
  };

  return data;
}

function _repository() {
  const data = _interopRequireDefault(require("../objects/repository"));

  _repository = function () {
    return data;
  };

  return data;
}

function _versionInvalid() {
  const data = _interopRequireDefault(require("../exceptions/version-invalid"));

  _versionInvalid = function () {
    return data;
  };

  return data;
}

function _logger() {
  const data = _interopRequireDefault(require("../../logger/logger"));

  _logger = function () {
    return data;
  };

  return data;
}

function _versionValidator() {
  const data = _interopRequireDefault(require("../version-validator"));

  _versionValidator = function () {
    return data;
  };

  return data;
}

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

  _envExtension = function () {
    return data;
  };

  return data;
}

/**
 * Represent a version model in the scope
 */
class Version extends _objects().BitObject {
  constructor(props) {
    super();
    this.mainFile = props.mainFile;
    this.files = props.files;
    this.dists = props.dists;
    this.mainDistFile = props.mainDistFile;
    this.compiler = props.compiler;
    this.tester = props.tester;
    this.log = props.log;
    this.dependencies = new (_dependencies().Dependencies)(props.dependencies);
    this.devDependencies = new (_dependencies().Dependencies)(props.devDependencies);
    this.compilerDependencies = new (_dependencies().Dependencies)(props.compilerDependencies);
    this.testerDependencies = new (_dependencies().Dependencies)(props.testerDependencies);
    this.docs = props.docs;
    this.ci = props.ci || {};
    this.specsResults = props.specsResults;
    this.flattenedDependencies = props.flattenedDependencies || new (_bitId().BitIds)();
    this.flattenedDevDependencies = props.flattenedDevDependencies || new (_bitId().BitIds)();
    this.flattenedCompilerDependencies = props.flattenedCompilerDependencies || new (_bitId().BitIds)();
    this.flattenedTesterDependencies = props.flattenedTesterDependencies || new (_bitId().BitIds)();
    this.packageDependencies = props.packageDependencies || {};
    this.devPackageDependencies = props.devPackageDependencies || {};
    this.peerPackageDependencies = props.peerPackageDependencies || {};
    this.compilerPackageDependencies = props.compilerPackageDependencies || {};
    this.testerPackageDependencies = props.testerPackageDependencies || {};
    this.bindingPrefix = props.bindingPrefix;
    this.customResolvedPaths = props.customResolvedPaths;
    this.overrides = props.overrides || {};
    this.packageJsonChangedProps = props.packageJsonChangedProps || {};
    this.validateVersion();
  }

  validateVersion() {
    const nonEmptyFields = ['mainFile', 'files'];
    nonEmptyFields.forEach(field => {
      // $FlowFixMe
      if (!this[field]) {
        throw new (_versionInvalid().default)(`failed creating a version object, the field "${field}" can't be empty`);
      }
    });
  }

  id() {
    const obj = this.toObject(); // @todo: remove the entire dependencies.relativePaths from the ID (it's going to be a breaking change)

    const getDependencies = deps => {
      const clonedDependencies = deps.cloneAsString();
      return clonedDependencies.map(dependency => {
        return {
          id: dependency.id,
          relativePaths: dependency.relativePaths.map(relativePath => {
            return {
              sourceRelativePath: relativePath.sourceRelativePath,
              destinationRelativePath: relativePath.destinationRelativePath
            };
          })
        };
      });
    };

    const filterFunction = (val, key) => {
      if (key === 'devDependencies' || key === 'compilerDependencies' || key === 'testerDependencies' || key === 'devPackageDependencies' || key === 'peerPackageDependencies' || key === 'compilerPackageDependencies' || key === 'testerPackageDependencies' || key === 'overrides') {
        return !_ramda().default.isEmpty(val);
      }

      return !!val;
    };

    return JSON.stringify((0, _utils().filterObject)({
      mainFile: obj.mainFile,
      files: obj.files,
      compiler: obj.compiler,
      tester: obj.tester,
      log: obj.log,
      dependencies: getDependencies(this.dependencies),
      devDependencies: getDependencies(this.devDependencies),
      compilerDependencies: getDependencies(this.compilerDependencies),
      testerDependencies: getDependencies(this.testerDependencies),
      packageDependencies: obj.packageDependencies,
      devPackageDependencies: obj.devPackageDependencies,
      peerPackageDependencies: obj.peerPackageDependencies,
      compilerPackageDependencies: obj.compilerPackageDependencies,
      testerPackageDependencies: obj.testerPackageDependencies,
      bindingPrefix: obj.bindingPrefix,
      overrides: obj.overrides
    }, filterFunction));
  }

  getAllFlattenedDependencies() {
    return _bitId().BitIds.fromArray([...this.flattenedDependencies, ...this.flattenedDevDependencies, ...this.flattenedCompilerDependencies, ...this.flattenedTesterDependencies]);
  }

  getAllDependencies() {
    return [...this.dependencies.dependencies, ...this.devDependencies.dependencies, ...this.compilerDependencies.dependencies, ...this.testerDependencies.dependencies];
  }

  getAllDependenciesCloned() {
    const dependencies = [...this.dependencies.getClone(), ...this.devDependencies.getClone(), ...this.compilerDependencies.getClone(), ...this.testerDependencies.getClone()];
    return new (_dependencies().Dependencies)(dependencies);
  }

  updateFlattenedDependency(currentId, newId) {
    const getUpdated = flattenedDependencies => {
      const updatedIds = flattenedDependencies.map(depId => {
        if (depId.isEqual(currentId)) return newId;
        return depId;
      });
      return _bitId().BitIds.fromArray(updatedIds);
    };

    this.flattenedDependencies = getUpdated(this.flattenedDependencies);
    this.flattenedDevDependencies = getUpdated(this.flattenedDevDependencies);
    this.flattenedCompilerDependencies = getUpdated(this.flattenedCompilerDependencies);
    this.flattenedTesterDependencies = getUpdated(this.flattenedTesterDependencies);
  }

  refs() {
    const extractRefsFromFiles = files => {
      const refs = files ? files.map(file => file.file) : [];
      return refs;
    };

    const files = extractRefsFromFiles(this.files);
    const dists = extractRefsFromFiles(this.dists);
    const compilerFiles = this.compiler ? extractRefsFromFiles(this.compiler.files) : [];
    const testerFiles = this.tester ? extractRefsFromFiles(this.tester.files) : [];
    return [...dists, ...files, ...compilerFiles, ...testerFiles].filter(ref => ref);
  }

  toObject() {
    const _convertFileToObject = file => {
      return {
        file: file.file.toString(),
        relativePath: file.relativePath,
        name: file.name,
        test: file.test
      };
    };

    const _convertEnvToObject = env => {
      if (typeof env === 'string') {
        return env;
      } // Store the env as string in case there is no config and files (for backward compatibility)


      if (envNameOnly(env)) {
        return env.name;
      }

      const result = {
        name: env.name,
        config: env.config,
        files: []
      };

      if (env.files && !_ramda().default.isEmpty(env.files)) {
        result.files = env.files.map(_extensionFile().default.fromModelObjectToObject);
      }

      return result;
    };

    return (0, _utils().filterObject)({
      files: this.files ? this.files.map(_convertFileToObject) : null,
      mainFile: this.mainFile,
      dists: this.dists ? this.dists.map(_convertFileToObject) : null,
      mainDistFile: this.mainDistFile,
      compiler: this.compiler ? _convertEnvToObject(this.compiler) : null,
      bindingPrefix: this.bindingPrefix || _constants().DEFAULT_BINDINGS_PREFIX,
      tester: this.tester ? _convertEnvToObject(this.tester) : null,
      log: {
        message: this.log.message,
        date: this.log.date,
        username: this.log.username,
        email: this.log.email
      },
      ci: this.ci,
      specsResults: this.specsResults,
      docs: this.docs,
      dependencies: this.dependencies.cloneAsObject(),
      devDependencies: this.devDependencies.cloneAsObject(),
      compilerDependencies: this.compilerDependencies.cloneAsObject(),
      testerDependencies: this.testerDependencies.cloneAsObject(),
      flattenedDependencies: this.flattenedDependencies.map(dep => dep.serialize()),
      flattenedDevDependencies: this.flattenedDevDependencies.map(dep => dep.serialize()),
      flattenedCompilerDependencies: this.flattenedCompilerDependencies.map(dep => dep.serialize()),
      flattenedTesterDependencies: this.flattenedTesterDependencies.map(dep => dep.serialize()),
      packageDependencies: this.packageDependencies,
      devPackageDependencies: this.devPackageDependencies,
      peerPackageDependencies: this.peerPackageDependencies,
      compilerPackageDependencies: this.compilerPackageDependencies,
      testerPackageDependencies: this.testerPackageDependencies,
      customResolvedPaths: this.customResolvedPaths,
      overrides: this.overrides,
      packageJsonChangedProps: this.packageJsonChangedProps
    }, val => !!val);
  }

  validateBeforePersisting(versionStr) {
    _logger().default.debug('validating version object: ', this.hash().hash);

    const version = Version.parse(versionStr);
    version.validate();
  }

  toBuffer(pretty) {
    const obj = this.toObject();
    const args = (0, _utils().getStringifyArgs)(pretty);
    const str = JSON.stringify(obj, ...args);
    if (this.validateBeforePersist) this.validateBeforePersisting(str);
    return Buffer.from(str);
  }
  /**
   * used by the super class BitObject
   */


  static parse(contents) {
    const {
      mainFile,
      dists,
      mainDistFile,
      files,
      compiler,
      bindingPrefix,
      tester,
      log,
      docs,
      ci,
      specsResults,
      dependencies,
      devDependencies,
      compilerDependencies,
      testerDependencies,
      flattenedDependencies,
      flattenedDevDependencies,
      flattenedCompilerDependencies,
      flattenedTesterDependencies,
      devPackageDependencies,
      peerPackageDependencies,
      compilerPackageDependencies,
      testerPackageDependencies,
      packageDependencies,
      customResolvedPaths,
      overrides,
      packageJsonChangedProps
    } = JSON.parse(contents);

    const _getDependencies = (deps = []) => {
      if (deps.length && _ramda().default.is(String, (0, _utils().first)(deps))) {
        // backward compatibility
        return deps.map(dependency => ({
          id: _bitId().BitId.parseObsolete(dependency)
        }));
      }

      const getRelativePath = relativePath => {
        if (relativePath.importSpecifiers) {
          // backward compatibility. Before the massive validation was added, an item of
          // relativePath.importSpecifiers array could be missing the mainFile property, which is
          // an invalid ImportSpecifier. (instead the mainFile it had another importSpecifiers object).
          relativePath.importSpecifiers = relativePath.importSpecifiers.filter(importSpecifier => importSpecifier.mainFile);
          if (!relativePath.importSpecifiers.length) delete relativePath.importSpecifiers;
        }

        return relativePath;
      };

      return deps.map(dependency => {
        return {
          id: _bitId().BitId.parseBackwardCompatible(dependency.id),
          relativePaths: Array.isArray(dependency.relativePaths) ? dependency.relativePaths.map(getRelativePath) : dependency.relativePaths
        };
      });
    };

    const _getFlattenedDependencies = (deps = []) => {
      return _bitId().BitIds.fromArray(deps.map(dep => _bitId().BitId.parseBackwardCompatible(dep)));
    };

    const parseFile = file => {
      return {
        file: _objects().Ref.from(file.file),
        relativePath: file.relativePath,
        name: file.name,
        test: file.test
      };
    };

    return new Version({
      mainFile,
      files: files ? files.map(parseFile) : null,
      dists: dists ? dists.map(parseFile) : null,
      mainDistFile,
      compiler: compiler ? parseEnv(compiler) : null,
      bindingPrefix: bindingPrefix || null,
      tester: tester ? parseEnv(tester) : null,
      log: {
        message: log.message,
        date: log.date,
        username: log.username,
        email: log.email
      },
      ci,
      specsResults,
      docs,
      dependencies: _getDependencies(dependencies),
      devDependencies: _getDependencies(devDependencies),
      compilerDependencies: _getDependencies(compilerDependencies),
      testerDependencies: _getDependencies(testerDependencies),
      flattenedDependencies: _getFlattenedDependencies(flattenedDependencies),
      flattenedDevDependencies: _getFlattenedDependencies(flattenedDevDependencies),
      flattenedCompilerDependencies: _getFlattenedDependencies(flattenedCompilerDependencies),
      flattenedTesterDependencies: _getFlattenedDependencies(flattenedTesterDependencies),
      devPackageDependencies,
      peerPackageDependencies,
      compilerPackageDependencies,
      testerPackageDependencies,
      packageDependencies,
      customResolvedPaths,
      overrides,
      packageJsonChangedProps
    });
  }
  /**
   * used by raw-object.toRealObject()
   */


  static from(versionProps) {
    return Version.parse(JSON.stringify(versionProps));
  }
  /**
   * Create version model object from consumer component
   * @param {*} param0
   */


  static fromComponent({
    component,
    versionFromModel,
    files,
    dists,
    mainDistFile,
    flattenedDependencies,
    flattenedDevDependencies,
    flattenedCompilerDependencies,
    flattenedTesterDependencies,
    message,
    specsResults,
    username,
    email
  }) {
    const parseFile = file => {
      return {
        file: file.file.hash(),
        relativePath: file.relativePath,
        name: file.name,
        test: file.test
      };
    };

    const compiler = component.compiler ? component.compiler.toModelObject() : undefined;
    const tester = component.tester ? component.tester.toModelObject() : undefined;
    const compilerDynamicPakageDependencies = component.compiler ? component.compiler.dynamicPackageDependencies : undefined;
    const testerDynamicPakageDependencies = component.tester ? component.tester.dynamicPackageDependencies : undefined;

    const compilerFromModel = _ramda().default.path(['compiler'], versionFromModel);

    const testerFromModel = _ramda().default.path(['tester'], versionFromModel);

    return new Version({
      mainFile: component.mainFile,
      files: files.map(parseFile),
      dists: dists ? dists.map(parseFile) : null,
      mainDistFile,
      compiler,
      bindingPrefix: component.bindingPrefix,
      tester,
      log: {
        message,
        username,
        email,
        date: Date.now().toString()
      },
      specsResults,
      docs: component.docs,
      dependencies: component.dependencies.get(),
      devDependencies: component.devDependencies.get(),
      compilerDependencies: component.compilerDependencies.get(),
      testerDependencies: component.testerDependencies.get(),
      packageDependencies: component.packageDependencies,
      devPackageDependencies: component.devPackageDependencies,
      peerPackageDependencies: component.peerPackageDependencies,
      compilerPackageDependencies: (0, _objectSpread2().default)({}, component.compilerPackageDependencies, compilerDynamicPakageDependencies),
      testerPackageDependencies: (0, _objectSpread2().default)({}, component.testerPackageDependencies, testerDynamicPakageDependencies),
      flattenedDependencies,
      flattenedDevDependencies,
      flattenedCompilerDependencies,
      flattenedTesterDependencies,
      customResolvedPaths: component.customResolvedPaths,
      overrides: component.overrides.componentOverridesData,
      packageJsonChangedProps: component.packageJsonChangedProps
    });
  }

  setSpecsResults(specsResults) {
    this.specsResults = specsResults;
  }

  setDist(dist) {
    this.dist = dist ? {
      file: dist.hash(),
      name: _constants().DEFAULT_BUNDLE_FILENAME
    } : null;
  }

  setCIProps(ci) {
    this.ci = ci;
  }

  modelFilesToSourceFiles(repository) {
    return Promise.all(this.files.map(file => _sources().SourceFile.loadFromSourceFileModel(file, repository)));
  }
  /**
   * Validate the version model properties, to make sure we are not inserting something
   * in the wrong format
   */


  validate() {
    (0, _versionValidator().default)(this);
  }

}

exports.default = Version;

const parseEnv = env => {
  if (typeof env === 'string') {
    return env;
  } // Store the env as string in case there is no config and files (for backward compatibility)


  if (envNameOnly(env)) {
    return env.name;
  }

  return {
    name: env.name,
    config: env.config,
    files: env.files ? env.files.map(_extensionFile().default.fromObjectToModelObject) : []
  };
};

const envNameOnly = env => {
  if ((!env.config || _ramda().default.isEmpty(env.config)) && (!env.files || _ramda().default.isEmpty(env.files))) {
    return true;
  }

  return false;
};

const getEnvModelOrName = env => {
  if (typeof env === 'string') {
    return env;
  } // Store the env as string in case there is no config and files (for backward compatibility)


  if (envNameOnly(env)) {
    return env.name;
  }

  return env;
};