"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.BitMap = void 0;
function _lodash() {
  const data = require("lodash");
  _lodash = function () {
    return data;
  };
  return data;
}
function _legacy() {
  const data = require("@teambit/legacy.bit-map");
  _legacy = function () {
    return data;
  };
  return data;
}
function _config() {
  const data = require("@teambit/legacy/dist/consumer/config");
  _config = function () {
    return data;
  };
  return data;
}
function _bitError() {
  const data = require("@teambit/bit-error");
  _bitError = function () {
    return data;
  };
  return data;
}
function _envs() {
  const data = require("@teambit/envs");
  _envs = function () {
    return data;
  };
  return data;
}
function _toolboxFs() {
  const data = require("@teambit/toolbox.fs.last-modified");
  _toolboxFs = function () {
    return data;
  };
  return data;
}
/**
 * consider extracting to a new component.
 * (pro: making Workspace aspect smaller. con: it's an implementation details of the workspace)
 */
class BitMap {
  constructor(legacyBitMap, consumer) {
    this.legacyBitMap = legacyBitMap;
    this.consumer = consumer;
  }
  mergeBitmaps(bitmapContent, otherBitmapContent, opts = {}) {
    return _legacy().BitMap.mergeContent(bitmapContent, otherBitmapContent, opts);
  }
  getPath() {
    return this.legacyBitMap.mapPath;
  }
  getAllRootDirs() {
    return Object.keys(this.legacyBitMap.getAllTrackDirs());
  }

  /**
   * adds component config to the .bitmap file.
   * later, upon `bit tag`, the data is saved in the scope.
   * returns a boolean indicating whether a change has been made.
   */
  addComponentConfig(id, aspectId, config = {}, shouldMergeConfig = false) {
    if (!aspectId || typeof aspectId !== 'string') throw new Error(`expect aspectId to be string, got ${aspectId}`);
    const bitMapEntry = this.getBitmapEntry(id, {
      ignoreVersion: true
    });
    const currentConfig = (bitMapEntry.config ||= {})[aspectId];
    if ((0, _lodash().isEqual)(currentConfig, config)) {
      return false; // no changes
    }
    const getNewConfig = () => {
      if (!config) return null;
      if (!shouldMergeConfig) return config;
      // should merge
      if (!currentConfig) return config;
      if (currentConfig === '-') return config;
      // lodash merge performs a deep merge. (the native concatenation don't)
      return (0, _lodash().merge)(currentConfig, config);
    };
    const newConfig = getNewConfig();
    if (newConfig) {
      bitMapEntry.config[aspectId] = newConfig;
    } else {
      delete bitMapEntry.config[aspectId];
    }
    this.legacyBitMap.markAsChanged();
    return true; // changes have been made
  }
  updateDefaultScope(oldScope, newScope) {
    const changedId = [];
    this.legacyBitMap.components.forEach(componentMap => {
      // only new components (not snapped/tagged) can be changed
      if (componentMap.defaultScope === oldScope && !componentMap.id.hasVersion()) {
        componentMap.defaultScope = newScope;
        componentMap.id = componentMap.id.changeDefaultScope(newScope);
        changedId.push(componentMap.id);
      }
    });
    if (changedId.length) {
      this.legacyBitMap.markAsChanged();
    }
    return changedId;
  }
  markAsChanged() {
    this.legacyBitMap.markAsChanged();
  }
  removeComponentConfig(id, aspectId, markWithMinusIfNotExist) {
    if (!aspectId || typeof aspectId !== 'string') throw new Error(`expect aspectId to be string, got ${aspectId}`);
    const bitMapEntry = this.getBitmapEntry(id, {
      ignoreVersion: true
    });
    const currentConfig = (bitMapEntry.config ||= {})[aspectId];
    if (currentConfig) {
      delete bitMapEntry.config[aspectId];
    } else {
      if (!markWithMinusIfNotExist) {
        return false; // no changes
      }
      bitMapEntry.config[aspectId] = _config().REMOVE_EXTENSION_SPECIAL_SIGN;
    }
    this.legacyBitMap.markAsChanged();
    return true; // changes have been made
  }
  removeEntireConfig(id) {
    const bitMapEntry = this.getBitmapEntry(id, {
      ignoreVersion: true
    });
    if (!bitMapEntry.config) return false;
    delete bitMapEntry.config;
    this.legacyBitMap.markAsChanged();
    return true;
  }
  setEntireConfig(id, config) {
    const bitMapEntry = this.getBitmapEntry(id, {
      ignoreVersion: true
    });
    bitMapEntry.config = config;
    this.legacyBitMap.markAsChanged();
  }
  removeDefaultScope(id) {
    const bitMapEntry = this.getBitmapEntry(id, {
      ignoreVersion: true
    });
    if (bitMapEntry.defaultScope) {
      delete bitMapEntry.defaultScope;
      this.legacyBitMap.markAsChanged();
    }
  }
  setDefaultScope(id, defaultScope) {
    const bitMapEntry = this.getBitmapEntry(id, {
      ignoreVersion: true
    });
    bitMapEntry.defaultScope = defaultScope;
    bitMapEntry.id = bitMapEntry.id.changeDefaultScope(defaultScope);
    this.legacyBitMap.markAsChanged();
  }
  setLocalOnly(ids) {
    ids.forEach(id => {
      const bitMapEntry = this.getBitmapEntry(id);
      bitMapEntry.localOnly = true;
    });
    this.legacyBitMap.markAsChanged();
  }
  unsetLocalOnly(ids) {
    const successfullyUnset = [];
    ids.forEach(id => {
      const bitMapEntry = this.getBitmapEntry(id);
      if (!bitMapEntry.localOnly) return;
      bitMapEntry.localOnly = false;
      successfullyUnset.push(id);
    });
    this.legacyBitMap.markAsChanged();
    return successfullyUnset;
  }
  listLocalOnly() {
    const allIds = this.legacyBitMap.getAllBitIds();
    return allIds.filter(id => this.getBitmapEntry(id).localOnly);
  }

  /**
   * write .bitmap object to the filesystem
   * optionally pass a reason for the change to be saved in the local scope `bitmap-history-metadata.txt` file.
   */
  async write(reasonForChange) {
    await this.consumer.writeBitMap(reasonForChange);
  }

  /**
   * get the data saved in the .bitmap file for this component-id.
   * throws if not found
   * @see this.getBitmapEntryIfExist
   */
  getBitmapEntry(id, {
    ignoreVersion
  } = {}) {
    return this.legacyBitMap.getComponent(id, {
      ignoreVersion
    });
  }
  getBitmapEntryIfExist(id, {
    ignoreVersion
  } = {}) {
    return this.legacyBitMap.getComponentIfExist(id, {
      ignoreVersion
    });
  }
  getAspectIdFromConfig(componentId, aspectId, ignoreAspectVersion = false) {
    const bitMapEntry = this.getBitmapEntry(componentId);
    const config = bitMapEntry.config;
    if (!config) {
      return undefined;
    }
    if (config[aspectId.toString()]) {
      return aspectId.toString();
    }
    if (!ignoreAspectVersion) {
      return undefined;
    }
    const allVersions = Object.keys(config).filter(id => id.startsWith(`${aspectId.toStringWithoutVersion()}@`));
    if (allVersions.length > 1) {
      throw new (_bitError().BitError)(`error: the same aspect ${aspectId.toStringWithoutVersion} configured multiple times for "${componentId.toString()}"\n${allVersions.join('\n')}`);
    }
    return allVersions.length === 1 ? allVersions[0] : undefined;
  }

  /**
   * components that were not tagged yet are safe to rename them from the .bitmap file.
   */
  renameNewComponent(sourceId, targetId) {
    const bitMapEntry = this.getBitmapEntry(sourceId);
    if (bitMapEntry.id.hasVersion()) {
      throw new Error(`unable to rename tagged or exported component: ${bitMapEntry.id.toString()}`);
    }
    if (sourceId.isEqual(targetId)) {
      throw new Error(`source-id and target-id are equal: "${sourceId.toString()}"`);
    }
    if (sourceId.fullName !== targetId.fullName) {
      this.legacyBitMap.removeComponent(bitMapEntry.id);
      bitMapEntry.id = targetId;
      if (sourceId.scope !== targetId.scope) bitMapEntry.defaultScope = targetId.scope;
      this.legacyBitMap.setComponent(bitMapEntry.id, bitMapEntry);
    } else if (sourceId.scope !== targetId.scope) {
      this.setDefaultScope(sourceId, targetId.scope);
    }
  }

  /**
   * helpful when reaming an aspect and this aspect is used in the config of other components.
   */
  renameAspectInConfig(sourceId, targetId) {
    this.legacyBitMap.components.forEach(componentMap => {
      const config = componentMap.config;
      if (!config) return;
      Object.keys(config).forEach(aspectId => {
        if (aspectId === sourceId.toString()) {
          config[targetId.toString()] = config[aspectId];
          delete config[aspectId];
          this.markAsChanged();
        }
        if (aspectId === _envs().EnvsAspect.id) {
          const envConfig = config[aspectId];
          if (envConfig !== '-' && envConfig.env === sourceId.toString()) {
            envConfig.env = targetId.toString();
            this.markAsChanged();
          }
        }
      });
      componentMap.config = config;
    });
  }
  removeComponent(id) {
    this.legacyBitMap.removeComponent(id);
  }

  /**
   * this is the lane-id of the recently exported lane. in case of a new lane, which was not exported yet, this will be
   * empty.
   */
  getExportedLaneId() {
    return this.legacyBitMap.isLaneExported ? this.legacyBitMap.laneId : undefined;
  }
  makeComponentsAvailableOnMain(ids) {
    ids.forEach(id => {
      const componentMap = this.getBitmapEntry(id);
      componentMap.isAvailableOnCurrentLane = true;
      delete componentMap.onLanesOnly;
    });
    this.legacyBitMap.markAsChanged();
  }

  /**
   * whether .bitmap file has changed in-memory
   */
  hasChanged() {
    return this.legacyBitMap.hasChanged;
  }
  takeSnapshot() {
    return this.legacyBitMap.components.map(comp => comp.clone());
  }
  restoreFromSnapshot(componentMaps) {
    this.legacyBitMap.components = componentMaps;
    this.legacyBitMap._invalidateCache();
  }

  /**
   * .bitmap file could be changed by other sources (e.g. manually or by "git pull") not only by bit.
   * this method returns the timestamp when the .bitmap has changed through bit. (e.g. as part of snap/tag/export/merge
   * process)
   */
  async getLastModifiedBitmapThroughBit() {
    const bitmapHistoryDir = this.consumer.getBitmapHistoryDir();
    const stat = await (0, _toolboxFs().getPathStatIfExist)(bitmapHistoryDir);
    if (!stat) return undefined;
    return stat.mtimeMs;
  }
}
exports.BitMap = BitMap;

//# sourceMappingURL=bit-map.js.map