'use strict';

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

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 _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

var _path;

function _load_path() {
  return _path = _interopRequireDefault(require('path'));
}

var _semver;

function _load_semver() {
  return _semver = _interopRequireDefault(require('semver'));
}

var _decamelize;

function _load_decamelize() {
  return _decamelize = _interopRequireDefault(require('decamelize'));
}

var _ramda;

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

var _version;

function _load_version() {
  return _version = _interopRequireDefault(require('../version'));
}

var _exceptions;

function _load_exceptions() {
  return _exceptions = require('./exceptions');
}

var _constants;

function _load_constants() {
  return _constants = require('../constants');
}

var _utils;

function _load_utils() {
  return _utils = require('../utils');
}

var _generalError;

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

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

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var BitId = function () {
  function BitId(_ref) {
    var scope = _ref.scope,
        box = _ref.box,
        name = _ref.name,
        version = _ref.version;

    _classCallCheck(this, BitId);

    if (!(0, (_utils || _load_utils()).isValidIdChunk)(name)) throw new (_exceptions || _load_exceptions()).InvalidName(name);
    if (scope && !(0, (_utils || _load_utils()).isValidScopeName)(scope)) throw new (_exceptions || _load_exceptions()).InvalidScopeName(scope);

    this.scope = scope || null;
    this.box = null;
    this.name = box ? `${box}/${name}` : name;
    this.version = version || null;
    Object.freeze(this);
  }

  _createClass(BitId, [{
    key: 'clone',
    value: function clone() {
      // $FlowFixMe
      return new BitId(this);
    }
  }, {
    key: 'changeScope',
    value: function changeScope(newScope) {
      return new BitId({ scope: newScope, name: this.name, version: this.version });
    }
  }, {
    key: 'changeVersion',
    value: function changeVersion(newVersion) {
      return new BitId({ scope: this.scope, name: this.name, version: newVersion });
    }
  }, {
    key: 'isLocal',
    value: function isLocal(scopeName) {
      return !this.scope || Boolean(scopeName && scopeName === this.scope);
    }
  }, {
    key: 'getVersion',
    value: function getVersion() {
      return (_version || _load_version()).default.parse(this.version);
    }
  }, {
    key: 'hasVersion',
    value: function hasVersion() {
      return Boolean(this.version && this.version !== (_constants || _load_constants()).LATEST_BIT_VERSION);
    }
  }, {
    key: 'hasScope',
    value: function hasScope() {
      return Boolean(this.scope);
    }
  }, {
    key: 'hasSameName',
    value: function hasSameName(id) {
      return this.name === id.name;
    }
  }, {
    key: 'hasSameScope',
    value: function hasSameScope(id) {
      if (this.hasScope() && id.hasScope()) return this.scope === id.scope;
      if (!this.hasScope() && !id.hasScope()) return true;
      return false; // one has scope but not the other
    }
  }, {
    key: 'hasSameVersion',
    value: function hasSameVersion(id) {
      if (this.hasVersion() && id.hasVersion()) return this.version === id.version;
      if (!this.hasVersion() && !id.hasVersion()) return true;
      return false; // one has version but not the other
    }
  }, {
    key: 'toString',
    value: function toString() {
      var ignoreScope = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
      var ignoreVersion = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
      var name = this.name,
          version = this.version;

      var scope = this.scope;
      var componentStr = ignoreScope || !scope ? name : [scope, name].join('/');
      // when there is no scope and the version is latest, omit the version.
      if (ignoreVersion || !this.hasVersion()) return componentStr;
      // $FlowFixMe version here is a string because this.hasVersion() is true
      return componentStr.concat(`${(_constants || _load_constants()).VERSION_DELIMITER}${version}`);
    }
  }, {
    key: 'toStringWithoutScope',
    value: function toStringWithoutScope() {
      return this.toString(true);
    }
  }, {
    key: 'toStringWithoutVersion',
    value: function toStringWithoutVersion() {
      return this.toString(false, true);
    }
  }, {
    key: 'toStringWithoutScopeAndVersion',
    value: function toStringWithoutScopeAndVersion() {
      return this.toString(true, true);
    }
  }, {
    key: 'isEqual',
    value: function isEqual(bitId) {
      return this.hasSameName(bitId) && this.hasSameScope(bitId) && this.hasSameVersion(bitId);
    }
  }, {
    key: 'isEqualWithoutVersion',
    value: function isEqualWithoutVersion(bitId) {
      return this.hasSameName(bitId) && this.hasSameScope(bitId);
    }
  }, {
    key: 'isEqualWithoutScopeAndVersion',
    value: function isEqualWithoutScopeAndVersion(bitId) {
      return this.hasSameName(bitId);
    }
  }, {
    key: 'serialize',
    value: function serialize() {
      var obj = { scope: this.scope, name: this.name, version: this.version };
      if (!this.hasVersion()) delete obj.version;
      if (!this.hasScope()) delete obj.scope;
      return obj;
    }
  }, {
    key: 'toObject',
    value: function toObject() {
      var key = this.scope ? [this.scope, this.name].join('/') : this.name;
      var value = this.version;

      return { [key]: value };
    }
  }, {
    key: 'toFullPath',
    value: function toFullPath() {
      if (!this.scope || !this.version) {
        throw new Error('BitId.toFullPath is unable to generate a path without a scope or a version');
      }
      return (_path || _load_path()).default.join(this.name, this.scope, this.version);
    }

    /**
     * Get a string id and return a string without the version part
     * @param {string} id
     * @return {string} id - id without version
     */

  }], [{
    key: 'getStringWithoutVersion',
    value: function getStringWithoutVersion(id) {
      return id.split((_constants || _load_constants()).VERSION_DELIMITER)[0];
    }
  }, {
    key: 'getVersionOnlyFromString',
    value: function getVersionOnlyFromString(id) {
      return id.split((_constants || _load_constants()).VERSION_DELIMITER)[1];
    }
  }, {
    key: 'parse',
    value: function parse(id) {
      var hasScope = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
      var version = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : (_constants || _load_constants()).LATEST_BIT_VERSION;

      if (!(_ramda || _load_ramda()).default.is(String, id)) {
        throw new TypeError(`BitId.parse expects to get "id" as a string, instead, got ${typeof id}`);
      }
      if (id.includes((_constants || _load_constants()).VERSION_DELIMITER)) {
        var _id$split = id.split((_constants || _load_constants()).VERSION_DELIMITER),
            _id$split2 = _slicedToArray(_id$split, 2),
            newId = _id$split2[0],
            newVersion = _id$split2[1];

        id = newId;
        version = newVersion;
      }

      if (hasScope) {
        var delimiterIndex = id.indexOf('/');
        if (delimiterIndex < 0) throw new (_exceptions || _load_exceptions()).InvalidBitId();
        var _scope = id.substring(0, delimiterIndex);
        var _name = id.substring(delimiterIndex + 1);
        return new BitId({
          scope: _scope,
          name: _name,
          version
        });
      }

      var name = id;
      return new BitId({
        name,
        version
      });
    }
  }, {
    key: 'parseObsolete',
    value: function parseObsolete(id) {
      var version = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : (_constants || _load_constants()).LATEST_BIT_VERSION;

      if (id.includes((_constants || _load_constants()).VERSION_DELIMITER)) {
        var _id$split3 = id.split((_constants || _load_constants()).VERSION_DELIMITER),
            _id$split4 = _slicedToArray(_id$split3, 2),
            newId = _id$split4[0],
            newVersion = _id$split4[1];

        id = newId;
        version = newVersion;
      }

      var idSplit = id.split('/');
      if (idSplit.length === 3) {
        var _idSplit = _slicedToArray(idSplit, 3),
            _scope2 = _idSplit[0],
            _box = _idSplit[1],
            _name2 = _idSplit[2];

        if (!(0, (_utils || _load_utils()).isValidIdChunk)(_name2, false) || !(0, (_utils || _load_utils()).isValidIdChunk)(_box, false) || !(0, (_utils || _load_utils()).isValidScopeName)(_scope2)) {
          // $FlowFixMe
          throw new (_exceptions || _load_exceptions()).InvalidIdChunk(`${_scope2}/${_box}/${_name2}`);
        }
        // $FlowFixMe (in this case the realScopeName is not null)
        return new BitId({
          scope: _scope2,
          box: _box,
          name: _name2,
          version
        });
      }

      if (idSplit.length === 2) {
        var _idSplit2 = _slicedToArray(idSplit, 2),
            _box2 = _idSplit2[0],
            _name3 = _idSplit2[1];

        if (!(0, (_utils || _load_utils()).isValidIdChunk)(_name3, false) || !(0, (_utils || _load_utils()).isValidIdChunk)(_box2, false)) {
          // $FlowFixMe
          throw new (_exceptions || _load_exceptions()).InvalidIdChunk(`${_box2}/${_name3}`);
        }
        // $FlowFixMe
        return new BitId({
          box: _box2,
          name: _name3,
          version
        });
      }

      if (idSplit.length === 1) {
        var _idSplit3 = _slicedToArray(idSplit, 1),
            _name4 = _idSplit3[0];

        if (!(0, (_utils || _load_utils()).isValidIdChunk)(_name4)) {
          // $FlowFixMe
          throw new (_exceptions || _load_exceptions()).InvalidIdChunk(_name4);
        }
        // $FlowFixMe
        return new BitId({
          name: _name4,
          version
        });
      }

      throw new (_exceptions || _load_exceptions()).InvalidBitId();
    }

    /**
     * before version 13.0.3 bitmap and component-dependencies ids were written as strings (e.g. scope/box/name@version)
     * since that version the ids are written as objects ({ scope: scopeName, name: compName, version: 0.0.1 })
     */

  }, {
    key: 'parseBackwardCompatible',
    value: function parseBackwardCompatible(id) {
      return typeof id === 'string' ? BitId.parseObsolete(id) : new BitId(id);
    }
  }, {
    key: 'getValidScopeName',
    value: function getValidScopeName(scope) {
      var suggestedName = scope.toLowerCase();
      var cleanName = suggestedName.split('').map(function (char) {
        if (/^[$\-_!.a-z0-9]+$/.test(char)) return char;
        return '';
      }).join('');

      // allow only one dot
      var nameSplitByDot = cleanName.split('.');
      if (nameSplitByDot.length > 1) {
        cleanName = `${(_ramda || _load_ramda()).default.head(nameSplitByDot)}.${(_ramda || _load_ramda()).default.tail(nameSplitByDot).join('')}`;
      }

      if (!cleanName) {
        throw new (_generalError || _load_generalError()).default('scope name created by directory name have to contains at least one character or number');
      }
      return cleanName;
    }
  }, {
    key: 'getValidBitId',
    value: function getValidBitId(box, name) {
      var getValidIdChunk = function getValidIdChunk(chunk) {
        if (!(0, (_utils || _load_utils()).isValidIdChunk)(chunk)) {
          chunk = chunk.replace(/\./g, '');
          chunk = (0, (_decamelize || _load_decamelize()).default)(chunk, '-');
        }
        return chunk;
      };

      return new BitId({ name: getValidIdChunk(name), box: getValidIdChunk(box) });
    }
  }, {
    key: 'isValidVersion',
    value: function isValidVersion(version) {
      return (_semver || _load_semver()).default.valid(version);
    }
  }]);

  return BitId;
}();

exports.default = BitId;