const getTypeOf = (thing) => typeof thing;
const EMPTY_STRING = '';
const STRING = getTypeOf(EMPTY_STRING);
const BOOLEAN = getTypeOf(true);
const NUMBER = getTypeOf(0);
const FUNCTION = getTypeOf(getTypeOf);
const NULL = 'null';
const SUM = 'sum';
const AVG = 'avg';
const MIN = 'min';
const MAX = 'max';
const LISTENER = 'Listener';
const RESULT = 'Result';
const GET = 'get';
const ADD = 'add';
const IDS = 'Ids';
const TABLE = 'Table';
const ROW = 'Row';
const ROW_COUNT = ROW + 'Count';
const ROW_IDS = ROW + IDS;
const SORTED_ROW_IDS = 'Sorted' + ROW + IDS;
const CELL = 'Cell';
const CELL_IDS = CELL + IDS;

const getIfNotFunction = (predicate) => (value, then, otherwise) =>
  predicate(value) ? otherwise?.() : then(value);
const math = Math;
const mathMax = math.max;
const mathMin = math.min;
const isFiniteNumber = isFinite;
const isUndefined = (thing) => thing === void 0;
const isNull = (thing) => thing === null;
const ifNotUndefined = getIfNotFunction(isUndefined);
const isTypeStringOrBoolean = (type) => type == STRING || type == BOOLEAN;
const isFunction = (thing) => getTypeOf(thing) == FUNCTION;
const isArray = (thing) => Array.isArray(thing);
const slice = (arrayOrString, start, end) => arrayOrString.slice(start, end);
const size = (arrayOrString) => arrayOrString.length;
const getUndefined = () => void 0;

const arrayEvery = (array, cb) => array.every(cb);
const arrayIsEqual = (array1, array2) =>
  size(array1) === size(array2) &&
  arrayEvery(array1, (value1, index) => array2[index] === value1);
const arrayForEach = (array, cb) => array.forEach(cb);
const arrayMap = (array, cb) => array.map(cb);
const arraySum = (array) => arrayReduce(array, (i, j) => i + j, 0);
const arrayIsEmpty = (array) => size(array) == 0;
const arrayReduce = (array, cb, initial) => array.reduce(cb, initial);
const arrayPush = (array, ...values) => array.push(...values);

const collSize = (coll) => coll?.size ?? 0;
const collHas = (coll, keyOrValue) => coll?.has(keyOrValue) ?? false;
const collIsEmpty = (coll) => isUndefined(coll) || collSize(coll) == 0;
const collValues = (coll) => [...(coll?.values() ?? [])];
const collClear = (coll) => coll.clear();
const collForEach = (coll, cb) => coll?.forEach(cb);
const collDel = (coll, keyOrValue) => coll?.delete(keyOrValue);

const object = Object;
const objEntries = object.entries;
const objFreeze = object.freeze;
const objNew = (entries = []) => object.fromEntries(entries);
const objToArray = (obj, cb) =>
  arrayMap(objEntries(obj), ([id, value]) => cb(value, id));
const objMap = (obj, cb) =>
  objNew(objToArray(obj, (value, id) => [id, cb(value, id)]));

const mapNew = (entries) => new Map(entries);
const mapKeys = (map) => [...(map?.keys() ?? [])];
const mapGet = (map, key) => map?.get(key);
const mapForEach = (map, cb) =>
  collForEach(map, (value, key) => cb(key, value));
const mapSet = (map, key, value) =>
  isUndefined(value) ? (collDel(map, key), map) : map?.set(key, value);
const mapEnsure = (map, key, getDefaultValue, hadExistingValue) => {
  if (!collHas(map, key)) {
    mapSet(map, key, getDefaultValue());
  } else {
    hadExistingValue?.(mapGet(map, key));
  }
  return mapGet(map, key);
};
const visitTree = (node, path, ensureLeaf, pruneLeaf, p = 0) =>
  ifNotUndefined(
    (ensureLeaf ? mapEnsure : mapGet)(
      node,
      path[p],
      p > size(path) - 2 ? ensureLeaf : mapNew,
    ),
    (nodeOrLeaf) => {
      if (p > size(path) - 2) {
        if (pruneLeaf?.(nodeOrLeaf)) {
          mapSet(node, path[p]);
        }
        return nodeOrLeaf;
      }
      const leaf = visitTree(nodeOrLeaf, path, ensureLeaf, pruneLeaf, p + 1);
      if (collIsEmpty(nodeOrLeaf)) {
        mapSet(node, path[p]);
      }
      return leaf;
    },
  );

const numericAggregators = /* @__PURE__ */ mapNew([
  [
    AVG,
    [
      (numbers, length) => arraySum(numbers) / length,
      (metric, add, length) => metric + (add - metric) / (length + 1),
      (metric, remove, length) => metric + (metric - remove) / (length - 1),
      (metric, add, remove, length) => metric + (add - remove) / length,
    ],
  ],
  [
    MAX,
    [
      (numbers) => mathMax(...numbers),
      (metric, add) => mathMax(add, metric),
      (metric, remove) => (remove == metric ? void 0 : metric),
      (metric, add, remove) =>
        remove == metric ? void 0 : mathMax(add, metric),
    ],
  ],
  [
    MIN,
    [
      (numbers) => mathMin(...numbers),
      (metric, add) => mathMin(add, metric),
      (metric, remove) => (remove == metric ? void 0 : metric),
      (metric, add, remove) =>
        remove == metric ? void 0 : mathMin(add, metric),
    ],
  ],
  [
    SUM,
    [
      (numbers) => arraySum(numbers),
      (metric, add) => metric + add,
      (metric, remove) => metric - remove,
      (metric, add, remove) => metric - remove + add,
    ],
  ],
]);
const getAggregateValue = (
  aggregateValue,
  oldLength,
  newValues,
  changedValues,
  aggregators,
  force = false,
) => {
  if (collIsEmpty(newValues)) {
    return void 0;
  }
  const [aggregate, aggregateAdd, aggregateRemove, aggregateReplace] =
    aggregators;
  force ||= isUndefined(aggregateValue);
  collForEach(changedValues, ([oldValue, newValue]) => {
    if (!force) {
      aggregateValue = isUndefined(oldValue)
        ? aggregateAdd?.(aggregateValue, newValue, oldLength++)
        : isUndefined(newValue)
          ? aggregateRemove?.(aggregateValue, oldValue, oldLength--)
          : aggregateReplace?.(aggregateValue, newValue, oldValue, oldLength);
      force ||= isUndefined(aggregateValue);
    }
  });
  return force
    ? aggregate(collValues(newValues), collSize(newValues))
    : aggregateValue;
};

const getCellOrValueType = (cellOrValue) => {
  if (isNull(cellOrValue)) {
    return NULL;
  }
  const type = getTypeOf(cellOrValue);
  return isTypeStringOrBoolean(type) ||
    (type == NUMBER && isFiniteNumber(cellOrValue))
    ? type
    : void 0;
};
const setOrDelCell = (store, tableId, rowId, cellId, cell) =>
  isUndefined(cell)
    ? store.delCell(tableId, rowId, cellId, true)
    : store.setCell(tableId, rowId, cellId, cell);

const setNew = (entryOrEntries) =>
  new Set(
    isArray(entryOrEntries) || isUndefined(entryOrEntries)
      ? entryOrEntries
      : [entryOrEntries],
  );
const setAdd = (set, value) => set?.add(value);

const getDefinableFunctions = (
  store,
  getDefaultThing,
  validateRowValue,
  addListener,
  callListeners,
) => {
  const hasRow = store.hasRow;
  const tableIds = mapNew();
  const things = mapNew();
  const thingIdListeners = mapNew();
  const allRowValues = mapNew();
  const allSortKeys = mapNew();
  const storeListenerIds = mapNew();
  const getStore = () => store;
  const getThingIds = () => mapKeys(tableIds);
  const forEachThing = (cb) => mapForEach(things, cb);
  const hasThing = (id) => collHas(things, id);
  const getTableId = (id) => mapGet(tableIds, id);
  const getThing = (id) => mapGet(things, id);
  const setThing = (id, thing) => mapSet(things, id, thing);
  const addStoreListeners = (id, andCall, ...listenerIds) => {
    const set = mapEnsure(storeListenerIds, id, setNew);
    arrayForEach(
      listenerIds,
      (listenerId) =>
        setAdd(set, listenerId) && andCall && store.callListener(listenerId),
    );
    return listenerIds;
  };
  const delStoreListeners = (id, ...listenerIds) =>
    ifNotUndefined(mapGet(storeListenerIds, id), (allListenerIds) => {
      arrayForEach(
        arrayIsEmpty(listenerIds) ? collValues(allListenerIds) : listenerIds,
        (listenerId) => {
          store.delListener(listenerId);
          collDel(allListenerIds, listenerId);
        },
      );
      if (collIsEmpty(allListenerIds)) {
        mapSet(storeListenerIds, id);
      }
    });
  const setDefinition = (id, tableId) => {
    mapSet(tableIds, id, tableId);
    if (!collHas(things, id)) {
      mapSet(things, id, getDefaultThing());
      mapSet(allRowValues, id, mapNew());
      mapSet(allSortKeys, id, mapNew());
      callListeners(thingIdListeners);
    }
  };
  const setDefinitionAndListen = (
    id,
    tableId,
    onChanged,
    getRowValue,
    getSortKey,
  ) => {
    setDefinition(id, tableId);
    const changedRowValues = mapNew();
    const changedSortKeys = mapNew();
    const rowValues = mapGet(allRowValues, id);
    const sortKeys = mapGet(allSortKeys, id);
    const processRow = (rowId) => {
      const getCell = (cellId) => store.getCell(tableId, rowId, cellId);
      const oldRowValue = mapGet(rowValues, rowId);
      const newRowValue = hasRow(tableId, rowId)
        ? validateRowValue(getRowValue(getCell, rowId))
        : void 0;
      if (
        !(
          oldRowValue === newRowValue ||
          (isArray(oldRowValue) &&
            isArray(newRowValue) &&
            arrayIsEqual(oldRowValue, newRowValue))
        )
      ) {
        mapSet(changedRowValues, rowId, [oldRowValue, newRowValue]);
      }
      if (!isUndefined(getSortKey)) {
        const oldSortKey = mapGet(sortKeys, rowId);
        const newSortKey = hasRow(tableId, rowId)
          ? getSortKey(getCell, rowId)
          : void 0;
        if (oldSortKey != newSortKey) {
          mapSet(changedSortKeys, rowId, newSortKey);
        }
      }
    };
    const processTable = (force) => {
      onChanged(
        () => {
          collForEach(changedRowValues, ([, newRowValue], rowId) =>
            mapSet(rowValues, rowId, newRowValue),
          );
          collForEach(changedSortKeys, (newSortKey, rowId) =>
            mapSet(sortKeys, rowId, newSortKey),
          );
        },
        changedRowValues,
        changedSortKeys,
        rowValues,
        sortKeys,
        force,
      );
      collClear(changedRowValues);
      collClear(changedSortKeys);
    };
    mapForEach(rowValues, processRow);
    if (store.hasTable(tableId)) {
      arrayForEach(store.getRowIds(tableId), (rowId) => {
        if (!collHas(rowValues, rowId)) {
          processRow(rowId);
        }
      });
    }
    processTable(true);
    delStoreListeners(id);
    addStoreListeners(
      id,
      0,
      store.addRowListener(tableId, null, (_store, _tableId, rowId) =>
        processRow(rowId),
      ),
      store.addTableListener(tableId, () => processTable()),
    );
  };
  const delDefinition = (id) => {
    mapSet(tableIds, id);
    mapSet(things, id);
    mapSet(allRowValues, id);
    mapSet(allSortKeys, id);
    delStoreListeners(id);
    callListeners(thingIdListeners);
  };
  const addThingIdsListener = (listener) =>
    addListener(listener, thingIdListeners);
  const destroy = () => mapForEach(storeListenerIds, delDefinition);
  return [
    getStore,
    getThingIds,
    forEachThing,
    hasThing,
    getTableId,
    getThing,
    setThing,
    setDefinition,
    setDefinitionAndListen,
    delDefinition,
    addThingIdsListener,
    destroy,
    addStoreListeners,
    delStoreListeners,
  ];
};
const getCreateFunction = (getFunction, initFunction) => {
  const thingsByStore = /* @__PURE__ */ new WeakMap();
  return (store) => {
    if (!thingsByStore.has(store)) {
      thingsByStore.set(store, getFunction(store));
    }
    const thing = thingsByStore.get(store);
    return thing;
  };
};

const createQueries = getCreateFunction((store) => {
  const createStore = store.createStore;
  const preStore = createStore();
  const resultStore = createStore();
  const preStoreListenerIds = mapNew();
  const {
    addListener,
    callListeners,
    delListener: delListenerImpl,
  } = resultStore;
  const [
    getStore,
    getQueryIds,
    forEachQuery,
    hasQuery,
    getTableId,
    ,
    ,
    setDefinition,
    ,
    delDefinition,
    addQueryIdsListenerImpl,
    destroy,
    addStoreListeners,
    delStoreListeners,
  ] = getDefinableFunctions(
    store,
    () => true,
    getUndefined,
    addListener,
    callListeners,
  );
  const addPreStoreListener = (preStore2, queryId, ...listenerIds) =>
    arrayForEach(listenerIds, (listenerId) =>
      setAdd(
        mapEnsure(
          mapEnsure(preStoreListenerIds, queryId, mapNew),
          preStore2,
          setNew,
        ),
        listenerId,
      ),
    );
  const resetPreStores = (queryId) => {
    ifNotUndefined(
      mapGet(preStoreListenerIds, queryId),
      (queryPreStoreListenerIds) => {
        mapForEach(queryPreStoreListenerIds, (preStore2, listenerIds) =>
          collForEach(listenerIds, (listenerId) =>
            preStore2.delListener(listenerId),
          ),
        );
        collClear(queryPreStoreListenerIds);
      },
    );
    arrayForEach([resultStore, preStore], (store2) => store2.delTable(queryId));
  };
  const synchronizeTransactions = (queryId, fromStore, toStore) =>
    addPreStoreListener(
      fromStore,
      queryId,
      fromStore.addStartTransactionListener(toStore.startTransaction),
      fromStore.addDidFinishTransactionListener(() =>
        toStore.finishTransaction(),
      ),
    );
  const setQueryDefinition = (queryId, tableId, build) => {
    setDefinition(queryId, tableId);
    resetPreStores(queryId);
    const selectEntries = [];
    const joinEntries = [[void 0, [tableId, void 0, void 0, [], mapNew()]]];
    const wheres = [];
    const groupEntries = [];
    const havings = [];
    const select = (arg1, arg2) => {
      const selectEntry = isFunction(arg1)
        ? [size(selectEntries) + EMPTY_STRING, arg1]
        : [
            isUndefined(arg2) ? arg1 : arg2,
            (getTableCell) => getTableCell(arg1, arg2),
          ];
      arrayPush(selectEntries, selectEntry);
      return {as: (selectedCellId) => (selectEntry[0] = selectedCellId)};
    };
    const join = (joinedTableId, arg1, arg2) => {
      const fromIntermediateJoinedTableId =
        isUndefined(arg2) || isFunction(arg1) ? void 0 : arg1;
      const onArg = isUndefined(fromIntermediateJoinedTableId) ? arg1 : arg2;
      const joinEntry = [
        joinedTableId,
        [
          joinedTableId,
          fromIntermediateJoinedTableId,
          isFunction(onArg) ? onArg : (getCell) => getCell(onArg),
          [],
          mapNew(),
        ],
      ];
      arrayPush(joinEntries, joinEntry);
      return {as: (joinedTableId2) => (joinEntry[0] = joinedTableId2)};
    };
    const where = (arg1, arg2, arg3) =>
      arrayPush(
        wheres,
        isFunction(arg1)
          ? arg1
          : isUndefined(arg3)
            ? (getTableCell) => getTableCell(arg1) === arg2
            : (getTableCell) => getTableCell(arg1, arg2) === arg3,
      );
    const group = (
      selectedCellId,
      aggregate,
      aggregateAdd,
      aggregateRemove,
      aggregateReplace,
    ) => {
      const groupEntry = [
        selectedCellId,
        [
          selectedCellId,
          isFunction(aggregate)
            ? [aggregate, aggregateAdd, aggregateRemove, aggregateReplace]
            : (mapGet(numericAggregators, aggregate) ?? [
                (_cells, length) => length,
              ]),
        ],
      ];
      arrayPush(groupEntries, groupEntry);
      return {as: (groupedCellId) => (groupEntry[0] = groupedCellId)};
    };
    const having = (arg1, arg2) =>
      arrayPush(
        havings,
        isFunction(arg1)
          ? arg1
          : (getSelectedOrGroupedCell) =>
              getSelectedOrGroupedCell(arg1) === arg2,
      );
    build({select, join, where, group, having});
    const selects = mapNew(selectEntries);
    if (collIsEmpty(selects)) {
      return queries;
    }
    const joins = mapNew(joinEntries);
    mapForEach(joins, (asTableId, [, fromAsTableId]) =>
      ifNotUndefined(mapGet(joins, fromAsTableId), ({3: toAsTableIds}) =>
        isUndefined(asTableId) ? 0 : arrayPush(toAsTableIds, asTableId),
      ),
    );
    const groups = mapNew(groupEntries);
    let selectJoinWhereStore = preStore;
    if (collIsEmpty(groups) && arrayIsEmpty(havings)) {
      selectJoinWhereStore = resultStore;
    } else {
      synchronizeTransactions(queryId, selectJoinWhereStore, resultStore);
      const groupedSelectedCellIds = mapNew();
      mapForEach(groups, (groupedCellId, [selectedCellId, aggregators]) =>
        setAdd(mapEnsure(groupedSelectedCellIds, selectedCellId, setNew), [
          groupedCellId,
          aggregators,
        ]),
      );
      const groupBySelectedCellIds = setNew();
      mapForEach(selects, (selectedCellId) =>
        collHas(groupedSelectedCellIds, selectedCellId)
          ? 0
          : setAdd(groupBySelectedCellIds, selectedCellId),
      );
      const tree = mapNew();
      const writeGroupRow = (
        leaf,
        changedGroupedSelectedCells,
        selectedRowId,
        forceRemove,
      ) =>
        ifNotUndefined(
          leaf,
          ([selectedCells, selectedRowIds, groupRowId, groupRow]) => {
            mapForEach(
              changedGroupedSelectedCells,
              (selectedCellId, [newCell]) => {
                const selectedCell = mapEnsure(
                  selectedCells,
                  selectedCellId,
                  mapNew,
                );
                const oldLeafCell = mapGet(selectedCell, selectedRowId);
                const newLeafCell = forceRemove ? void 0 : newCell;
                if (oldLeafCell !== newLeafCell) {
                  const oldNewSet = setNew([[oldLeafCell, newLeafCell]]);
                  const oldLength = collSize(selectedCell);
                  mapSet(selectedCell, selectedRowId, newLeafCell);
                  collForEach(
                    mapGet(groupedSelectedCellIds, selectedCellId),
                    ([groupedCellId, aggregators]) => {
                      const aggregateValue = getAggregateValue(
                        groupRow[groupedCellId],
                        oldLength,
                        selectedCell,
                        oldNewSet,
                        aggregators,
                      );
                      groupRow[groupedCellId] = isUndefined(
                        getCellOrValueType(aggregateValue),
                      )
                        ? void 0
                        : aggregateValue;
                    },
                  );
                }
              },
            );
            if (
              collIsEmpty(selectedRowIds) ||
              !arrayEvery(havings, (having2) =>
                having2((cellId) => groupRow[cellId]),
              )
            ) {
              resultStore.delRow(queryId, groupRowId);
            } else if (isUndefined(groupRowId)) {
              leaf[2] = resultStore.addRow(queryId, groupRow);
            } else {
              resultStore.setRow(queryId, groupRowId, groupRow);
            }
          },
        );
      addPreStoreListener(
        selectJoinWhereStore,
        queryId,
        selectJoinWhereStore.addRowListener(
          queryId,
          null,
          (_store, _tableId, selectedRowId, getCellChange) => {
            const oldPath = [];
            const newPath = [];
            const changedGroupedSelectedCells = mapNew();
            const rowExists = selectJoinWhereStore.hasRow(
              queryId,
              selectedRowId,
            );
            let changedLeaf = !rowExists;
            collForEach(groupBySelectedCellIds, (selectedCellId) => {
              const [changed, oldCell, newCell] = getCellChange(
                queryId,
                selectedRowId,
                selectedCellId,
              );
              arrayPush(oldPath, oldCell);
              arrayPush(newPath, newCell);
              changedLeaf ||= changed;
            });
            mapForEach(groupedSelectedCellIds, (selectedCellId) => {
              const [changed, , newCell] = getCellChange(
                queryId,
                selectedRowId,
                selectedCellId,
              );
              if (changedLeaf || changed) {
                mapSet(changedGroupedSelectedCells, selectedCellId, [newCell]);
              }
            });
            if (changedLeaf) {
              writeGroupRow(
                visitTree(tree, oldPath, void 0, ([, selectedRowIds]) => {
                  collDel(selectedRowIds, selectedRowId);
                  return collIsEmpty(selectedRowIds);
                }),
                changedGroupedSelectedCells,
                selectedRowId,
                1,
              );
            }
            if (rowExists) {
              writeGroupRow(
                visitTree(
                  tree,
                  newPath,
                  () => {
                    const groupRow = {};
                    collForEach(
                      groupBySelectedCellIds,
                      (selectedCellId) =>
                        (groupRow[selectedCellId] =
                          selectJoinWhereStore.getCell(
                            queryId,
                            selectedRowId,
                            selectedCellId,
                          )),
                    );
                    return [mapNew(), setNew(), void 0, groupRow];
                  },
                  ([, selectedRowIds]) => {
                    setAdd(selectedRowIds, selectedRowId);
                  },
                ),
                changedGroupedSelectedCells,
                selectedRowId,
              );
            }
          },
        ),
      );
    }
    synchronizeTransactions(queryId, store, selectJoinWhereStore);
    const writeSelectRow = (rootRowId) => {
      const getTableCell = (arg1, arg2) =>
        store.getCell(
          ...(isUndefined(arg2)
            ? [tableId, rootRowId, arg1]
            : arg1 === tableId
              ? [tableId, rootRowId, arg2]
              : [
                  mapGet(joins, arg1)?.[0],
                  mapGet(mapGet(joins, arg1)?.[4], rootRowId)?.[0],
                  arg2,
                ]),
        );
      selectJoinWhereStore.transaction(() =>
        arrayEvery(wheres, (where2) => where2(getTableCell))
          ? mapForEach(selects, (asCellId, tableCellGetter) =>
              setOrDelCell(
                selectJoinWhereStore,
                queryId,
                rootRowId,
                asCellId,
                tableCellGetter(getTableCell, rootRowId),
              ),
            )
          : selectJoinWhereStore.delRow(queryId, rootRowId),
      );
    };
    const listenToTable = (rootRowId, tableId2, rowId, joinedTableIds2) => {
      const getCell = (cellId) => store.getCell(tableId2, rowId, cellId);
      arrayForEach(joinedTableIds2, (remoteAsTableId) => {
        const [realJoinedTableId, , on, nextJoinedTableIds, remoteIdPairs] =
          mapGet(joins, remoteAsTableId);
        const remoteRowId = on?.(getCell, rootRowId);
        const [previousRemoteRowId, previousRemoteListenerId] =
          mapGet(remoteIdPairs, rootRowId) ?? [];
        if (remoteRowId != previousRemoteRowId) {
          if (!isUndefined(previousRemoteListenerId)) {
            delStoreListeners(queryId, previousRemoteListenerId);
          }
          mapSet(
            remoteIdPairs,
            rootRowId,
            isUndefined(remoteRowId)
              ? void 0
              : [
                  remoteRowId,
                  ...addStoreListeners(
                    queryId,
                    1,
                    store.addRowListener(realJoinedTableId, remoteRowId, () =>
                      listenToTable(
                        rootRowId,
                        realJoinedTableId,
                        remoteRowId,
                        nextJoinedTableIds,
                      ),
                    ),
                  ),
                ],
          );
        }
      });
      writeSelectRow(rootRowId);
    };
    const {3: joinedTableIds} = mapGet(joins, void 0);
    selectJoinWhereStore.transaction(() =>
      addStoreListeners(
        queryId,
        1,
        store.addRowListener(tableId, null, (_store, _tableId, rootRowId) => {
          if (store.hasRow(tableId, rootRowId)) {
            listenToTable(rootRowId, tableId, rootRowId, joinedTableIds);
          } else {
            selectJoinWhereStore.delRow(queryId, rootRowId);
            collForEach(joins, ({4: idsByRootRowId}) =>
              ifNotUndefined(
                mapGet(idsByRootRowId, rootRowId),
                ([, listenerId]) => {
                  delStoreListeners(queryId, listenerId);
                  mapSet(idsByRootRowId, rootRowId);
                },
              ),
            );
          }
        }),
      ),
    );
    return queries;
  };
  const delQueryDefinition = (queryId) => {
    resetPreStores(queryId);
    delDefinition(queryId);
    return queries;
  };
  const addQueryIdsListener = (listener) =>
    addQueryIdsListenerImpl(() => listener(queries));
  const delListener = (listenerId) => {
    delListenerImpl(listenerId);
    return queries;
  };
  const getListenerStats = () => {
    const {
      tables: _1,
      tableIds: _2,
      transaction: _3,
      ...stats
    } = resultStore.getListenerStats();
    return stats;
  };
  const queries = {
    setQueryDefinition,
    delQueryDefinition,
    getStore,
    getQueryIds,
    forEachQuery,
    hasQuery,
    getTableId,
    addQueryIdsListener,
    delListener,
    destroy,
    getListenerStats,
  };
  objMap(
    {
      [TABLE]: [1, 1],
      [TABLE + CELL_IDS]: [0, 1],
      [ROW_COUNT]: [0, 1],
      [ROW_IDS]: [0, 1],
      [SORTED_ROW_IDS]: [0, 5],
      [ROW]: [1, 2],
      [CELL_IDS]: [0, 2],
      [CELL]: [1, 3],
    },
    ([hasAndForEach, argumentCount], gettable) => {
      arrayForEach(
        hasAndForEach ? [GET, 'has', 'forEach'] : [GET],
        (prefix) =>
          (queries[prefix + RESULT + gettable] = (...args) =>
            resultStore[prefix + gettable](...args)),
      );
      queries[ADD + RESULT + gettable + LISTENER] = (...args) =>
        resultStore[ADD + gettable + LISTENER](
          ...slice(args, 0, argumentCount),
          (_store, ...listenerArgs) =>
            args[argumentCount](queries, ...listenerArgs),
          true,
        );
    },
  );
  return objFreeze(queries);
});

export {createQueries};
