"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
exports.__esModule = true;
exports.QueryResultState = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _model = require("../model");
var _mobx = require("mobx");
var _flatMap = _interopRequireDefault(require("lodash/flatMap"));
class QueryResultState {
  get pages() {
    this._pagesAtom.reportObserved();
    return this._pages.processed;
  }
  get _keyedItemsMap() {
    const {
      keyedItems
    } = this;
    const map = new Map();
    for (const keyedItem of keyedItems) {
      map.set(keyedItem.key, keyedItem);
    }
    return map;
  }
  constructor(params) {
    (0, _defineProperty2.default)(this, "itemKey", void 0);
    (0, _defineProperty2.default)(this, "_pagesAtom", (0, _mobx.createAtom)('QueryResultState.pages'));
    (0, _defineProperty2.default)(this, "_pages", {
      processed: [],
      unprocessed: []
    });
    (0, _defineProperty2.default)(this, "_totals", {
      processed: 0,
      unprocessed: 0
    });
    (0, _defineProperty2.default)(this, "_supportTotal", false);
    (0, _defineProperty2.default)(this, "fetchTotal", void 0);
    (0, _defineProperty2.default)(this, "available", null);
    (0, _defineProperty2.default)(this, "status", {
      ..._model.queryStatus.idle,
      error: null,
      isFetching: false
    });
    (0, _defineProperty2.default)(this, "originQuery", void 0);
    (0, _defineProperty2.default)(this, "setNewPages", (0, _mobx.action)(pages => {
      this._pages = pages;
      this._pagesAtom.reportChanged();
    }));
    this.itemKey = params.itemKey;
    this.originQuery = params.originQuery;
    this.fetchTotal = params.fetchTotal;
    (0, _mobx.makeObservable)(this, {
      items: (0, _mobx.computed)({
        keepAlive: true
      }),
      keyedItems: (0, _mobx.computed)({
        keepAlive: true
      }),
      _keyedItemsMap: _mobx.computed,
      pages: _mobx.computed,
      size: _mobx.computed,
      itemsKeys: _mobx.computed,
      fetchedAll: _mobx.computed,
      isFetching: _mobx.computed,
      isEmptyAndNotFetching: _mobx.computed,
      asComputed: _mobx.computed,
      total: _mobx.computed,
      hasAvailableItems: _mobx.computed,
      _totals: _mobx.observable.ref,
      _supportTotal: _mobx.observable.ref,
      setTotals: _mobx.action,
      available: _mobx.observable.ref,
      status: _mobx.observable.ref,
      originQuery: _mobx.observable.ref,
      changeItems: _mobx.action,
      totalStatus: _mobx.computed,
      slice: _mobx.action
    });
  }
  init() {
    var _this$fetchTotal;
    const disposers = [(_this$fetchTotal = this.fetchTotal) == null ? void 0 : _this$fetchTotal.init()];
    return () => {
      disposers.forEach(disposer => disposer == null ? void 0 : disposer());
    };
  }
  get errorStatus() {
    const {
      status
    } = this;
    return status.isError ? status : null;
  }
  get keyedItems() {
    this._pagesAtom.reportObserved();
    const {
      pages,
      itemKey
    } = this;
    let currentIndex = 0;
    return (0, _flatMap.default)(pages, (page, pageIndex) => {
      return page.map((item, indexWithinPage) => {
        const key = itemKey(item);
        return {
          key,
          id: key,
          item,
          index: currentIndex++,
          pageIndex,
          indexWithinPage
        };
      });
    });
  }
  get itemsKeys() {
    return this.keyedItems.map(({
      key
    }) => key);
  }
  get isEmptyAndNotFetching() {
    const {
      status,
      keyedItems
    } = this;
    return status.isSuccess && !status.isFetching && !keyedItems.length;
  }
  get isEmpty() {
    const {
      keyedItems
    } = this;
    return keyedItems.length === 0;
  }
  get items() {
    this._pagesAtom.reportObserved();
    const {
      pages
    } = this;
    return (0, _flatMap.default)(pages);
  }
  get total() {
    return this.fetchTotal ? this.fetchTotal.total : this._totals.processed;
  }
  get totalStatus() {
    return this.fetchTotal ? this.fetchTotal.status : this.status.status;
  }
  get supportTotal() {
    return !!this.fetchTotal || this._supportTotal;
  }
  get fetchedAll() {
    const {
      status,
      items,
      total
    } = this;
    return !status.isLoading && total <= items.length;
  }
  get isFetching() {
    return this.status.isFetching;
  }
  get(key) {
    this._pagesAtom.reportObserved();
    return this._keyedItemsMap.get(key);
  }
  has(key) {
    return this._keyedItemsMap.has(key);
  }
  createRollbackFn(updates) {
    return () => {
      const {
        itemKey,
        _keyedItemsMap,
        pages
      } = this;
      for (const {
        newItem,
        prevItem
      } of updates) {
        const key = itemKey(newItem);
        const keyedItem = _keyedItemsMap.get(key);
        // the current item is still the one that was recently updated
        if (keyedItem != null) {
          const {
            pageIndex,
            indexWithinPage
          } = keyedItem;
          const page = pages[pageIndex];
          if (page[indexWithinPage] === newItem) {
            this.changeItems([prevItem]);
            return true;
          }
        }
      }
      return false;
    };
  }
  changeItems(newItems) {
    const {
      itemKey,
      _pages,
      _pagesAtom,
      keyedItems
    } = this;
    const updates = [];
    for (const newItem of newItems) {
      const key = itemKey(newItem);
      const keyedItem = keyedItems.find(item => item.key === key);
      if (keyedItem != null) {
        const {
          pageIndex,
          indexWithinPage
        } = keyedItem;
        const processedPage = _pages.processed[pageIndex];
        // this mutates react-query cache since `page` is the same reference of the cached data
        // this behavior is intentional because the strategy is to assume the server will be update correctly in the next invalidation
        // until then, we simulate locally (cache) what we expect the server state eventually to be
        const prevItem = processedPage[indexWithinPage];
        processedPage[indexWithinPage] = newItem;
        _pages.unprocessed.forEach(page => {
          const itemIndex = page.findIndex(item => itemKey(item) === key);
          if (itemIndex !== -1) {
            page[itemIndex] = newItem;
          }
        });
        updates.push({
          prevItem,
          newItem
        });
      }
    }
    if (updates.length) {
      _pagesAtom.reportChanged();
      return {
        rollback: this.createRollbackFn(updates)
      };
    }
    return undefined;
  }
  addItems(newItems) {
    const {
      _pages,
      _pagesAtom
    } = this;
    _pages.processed = [newItems, ..._pages.processed];
    _pages.unprocessed = [newItems, ..._pages.unprocessed];
    _pagesAtom.reportChanged();
    return {
      rollback: () => {
        _pages.processed = _pages.processed.slice(1);
        _pages.unprocessed = _pages.unprocessed.slice(1);
        _pagesAtom.reportChanged();
      }
    };
  }
  clear() {
    this.setNewPages({
      processed: [],
      unprocessed: []
    });
  }
  addPages(morePages) {
    const {
      _pages
    } = this;
    this.setNewPages({
      unprocessed: [..._pages.unprocessed, ...morePages.unprocessed],
      processed: [..._pages.processed, ...morePages.processed]
    });
  }
  setTotals(totals) {
    this._totals = totals;
  }
  setSupportTotal(supportTotal) {
    this._supportTotal = supportTotal;
  }
  setPages(pages) {
    this.setNewPages(pages);
  }
  slice(start, end) {
    const {
      _pages
    } = this;
    this.setNewPages({
      unprocessed: _pages.unprocessed.slice(start, end),
      processed: _pages.processed.slice(start, end)
    });
  }
  get hasAvailableItems() {
    const {
      originQuery: {
        hasNonPersistentActiveFilters
      },
      available,
      total,
      size
    } = this;
    // if `available` info is missing, must assume that there are available items if non persistent filters are active
    return available != null ? available > 0 : hasNonPersistentActiveFilters || total > 0 || size > 0;
  }
  get asComputed() {
    const {
      total,
      available
    } = this;
    return {
      total,
      available: available ?? 0
    };
  }
  get size() {
    return this.items.length;
  }
}
exports.QueryResultState = QueryResultState;
//# sourceMappingURL=QueryResultState.js.map