"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const utils = require("../utils.cjs");
const SortedMap = require("../SortedMap.cjs");
const virtualProps = require("../virtual-props.cjs");
const transactionMetadata = require("./transaction-metadata.cjs");
class CollectionStateManager {
  /**
   * Creates a new CollectionState manager
   */
  constructor(config) {
    this.pendingSyncedTransactions = [];
    this.syncedMetadata = /* @__PURE__ */ new Map();
    this.syncedCollectionMetadata = /* @__PURE__ */ new Map();
    this.optimisticUpserts = /* @__PURE__ */ new Map();
    this.optimisticDeletes = /* @__PURE__ */ new Set();
    this.pendingOptimisticUpserts = /* @__PURE__ */ new Map();
    this.pendingOptimisticDeletes = /* @__PURE__ */ new Set();
    this.pendingOptimisticDirectUpserts = /* @__PURE__ */ new Set();
    this.pendingOptimisticDirectDeletes = /* @__PURE__ */ new Set();
    this.rowOrigins = /* @__PURE__ */ new Map();
    this.pendingLocalChanges = /* @__PURE__ */ new Set();
    this.pendingLocalOrigins = /* @__PURE__ */ new Set();
    this.virtualPropsCache = /* @__PURE__ */ new WeakMap();
    this.size = 0;
    this.syncedKeys = /* @__PURE__ */ new Set();
    this.preSyncVisibleState = /* @__PURE__ */ new Map();
    this.recentlySyncedKeys = /* @__PURE__ */ new Set();
    this.hasReceivedFirstCommit = false;
    this.isCommittingSyncTransactions = false;
    this.isLocalOnly = false;
    this.commitPendingTransactions = () => {
      let hasPersistingTransaction = false;
      for (const transaction of this.transactions.values()) {
        if (transaction.state === `persisting`) {
          hasPersistingTransaction = true;
          break;
        }
      }
      const {
        committedSyncedTransactions,
        uncommittedSyncedTransactions,
        hasTruncateSync,
        hasImmediateSync
      } = this.pendingSyncedTransactions.reduce(
        (acc, t) => {
          if (t.committed) {
            acc.committedSyncedTransactions.push(t);
            if (t.truncate) {
              acc.hasTruncateSync = true;
            }
            if (t.immediate) {
              acc.hasImmediateSync = true;
            }
          } else {
            acc.uncommittedSyncedTransactions.push(t);
          }
          return acc;
        },
        {
          committedSyncedTransactions: [],
          uncommittedSyncedTransactions: [],
          hasTruncateSync: false,
          hasImmediateSync: false
        }
      );
      if (!hasPersistingTransaction || hasTruncateSync || hasImmediateSync) {
        this.isCommittingSyncTransactions = true;
        const previousRowOrigins = new Map(this.rowOrigins);
        const previousOptimisticUpserts = new Map(this.optimisticUpserts);
        const previousOptimisticDeletes = new Set(this.optimisticDeletes);
        const truncateOptimisticSnapshot = hasTruncateSync ? committedSyncedTransactions.find((t) => t.truncate)?.optimisticSnapshot : null;
        let truncatePendingLocalChanges;
        let truncatePendingLocalOrigins;
        const changedKeys = /* @__PURE__ */ new Set();
        for (const transaction of committedSyncedTransactions) {
          for (const operation of transaction.operations) {
            changedKeys.add(operation.key);
          }
          for (const [key] of transaction.rowMetadataWrites) {
            changedKeys.add(key);
          }
        }
        let currentVisibleState = this.preSyncVisibleState;
        if (currentVisibleState.size === 0) {
          currentVisibleState = /* @__PURE__ */ new Map();
          for (const key of changedKeys) {
            const currentValue = this.get(key);
            if (currentValue !== void 0) {
              currentVisibleState.set(key, currentValue);
            }
          }
        }
        const events = [];
        const rowUpdateMode = this.config.sync.rowUpdateMode || `partial`;
        const completedOptimisticOps = /* @__PURE__ */ new Map();
        for (const transaction of this.transactions.values()) {
          if (transaction.state === `completed`) {
            for (const mutation of transaction.mutations) {
              if (this.isThisCollection(mutation.collection)) {
                if (mutation.optimistic) {
                  completedOptimisticOps.set(mutation.key, {
                    type: mutation.type,
                    value: mutation.modified
                  });
                }
              }
            }
          }
        }
        for (const transaction of committedSyncedTransactions) {
          if (transaction.truncate) {
            const visibleKeys = /* @__PURE__ */ new Set([
              ...this.syncedData.keys(),
              ...truncateOptimisticSnapshot?.upserts.keys() || []
            ]);
            for (const key of visibleKeys) {
              if (truncateOptimisticSnapshot?.deletes.has(key)) continue;
              const previousValue = truncateOptimisticSnapshot?.upserts.get(key) || this.syncedData.get(key);
              if (previousValue !== void 0) {
                events.push({ type: `delete`, key, value: previousValue });
              }
            }
            truncatePendingLocalChanges = new Set(this.pendingLocalChanges);
            truncatePendingLocalOrigins = new Set(this.pendingLocalOrigins);
            this.syncedData.clear();
            this.syncedMetadata.clear();
            this.syncedKeys.clear();
            this.clearOriginTrackingState();
            for (const key of changedKeys) {
              currentVisibleState.delete(key);
            }
            this._events.emit(`truncate`, {
              type: `truncate`,
              collection: this.collection
            });
          }
          for (const operation of transaction.operations) {
            const key = operation.key;
            this.syncedKeys.add(key);
            const origin = this.isLocalOnly || this.pendingLocalChanges.has(key) || this.pendingLocalOrigins.has(key) || truncatePendingLocalChanges?.has(key) === true || truncatePendingLocalOrigins?.has(key) === true ? "local" : "remote";
            switch (operation.type) {
              case `insert`:
                this.syncedData.set(key, operation.value);
                this.rowOrigins.set(key, origin);
                this.pendingLocalChanges.delete(key);
                this.pendingLocalOrigins.delete(key);
                this.pendingOptimisticUpserts.delete(key);
                this.pendingOptimisticDeletes.delete(key);
                this.pendingOptimisticDirectUpserts.delete(key);
                this.pendingOptimisticDirectDeletes.delete(key);
                break;
              case `update`: {
                if (rowUpdateMode === `partial`) {
                  const updatedValue = Object.assign(
                    {},
                    this.syncedData.get(key),
                    operation.value
                  );
                  this.syncedData.set(key, updatedValue);
                } else {
                  this.syncedData.set(key, operation.value);
                }
                this.rowOrigins.set(key, origin);
                this.pendingLocalChanges.delete(key);
                this.pendingLocalOrigins.delete(key);
                this.pendingOptimisticUpserts.delete(key);
                this.pendingOptimisticDeletes.delete(key);
                this.pendingOptimisticDirectUpserts.delete(key);
                this.pendingOptimisticDirectDeletes.delete(key);
                break;
              }
              case `delete`:
                this.syncedData.delete(key);
                this.syncedMetadata.delete(key);
                this.rowOrigins.delete(key);
                this.pendingLocalChanges.delete(key);
                this.pendingLocalOrigins.delete(key);
                this.pendingOptimisticUpserts.delete(key);
                this.pendingOptimisticDeletes.delete(key);
                this.pendingOptimisticDirectUpserts.delete(key);
                this.pendingOptimisticDirectDeletes.delete(key);
                break;
            }
          }
          for (const [key, metadataWrite] of transaction.rowMetadataWrites) {
            if (metadataWrite.type === `delete`) {
              this.syncedMetadata.delete(key);
              continue;
            }
            this.syncedMetadata.set(key, metadataWrite.value);
          }
          for (const [
            key,
            metadataWrite
          ] of transaction.collectionMetadataWrites) {
            if (metadataWrite.type === `delete`) {
              this.syncedCollectionMetadata.delete(key);
              continue;
            }
            this.syncedCollectionMetadata.set(key, metadataWrite.value);
          }
        }
        if (hasTruncateSync) {
          const syncedInsertedOrUpdatedKeys = /* @__PURE__ */ new Set();
          for (const t of committedSyncedTransactions) {
            for (const op of t.operations) {
              if (op.type === `insert` || op.type === `update`) {
                syncedInsertedOrUpdatedKeys.add(op.key);
              }
            }
          }
          const reapplyUpserts = new Map(
            truncateOptimisticSnapshot.upserts
          );
          const reapplyDeletes = new Set(
            truncateOptimisticSnapshot.deletes
          );
          for (const [key, value] of reapplyUpserts) {
            if (reapplyDeletes.has(key)) continue;
            if (syncedInsertedOrUpdatedKeys.has(key)) {
              let foundInsert = false;
              for (let i = events.length - 1; i >= 0; i--) {
                const evt = events[i];
                if (evt.key === key && evt.type === `insert`) {
                  evt.value = value;
                  foundInsert = true;
                  break;
                }
              }
              if (!foundInsert) {
                events.push({ type: `insert`, key, value });
              }
            } else {
              events.push({ type: `insert`, key, value });
            }
          }
          if (events.length > 0 && reapplyDeletes.size > 0) {
            const filtered = [];
            for (const evt of events) {
              if (evt.type === `insert` && reapplyDeletes.has(evt.key)) {
                continue;
              }
              filtered.push(evt);
            }
            events.length = 0;
            events.push(...filtered);
          }
          if (this.lifecycle.status !== `ready`) {
            this.lifecycle.markReady();
          }
        }
        this.optimisticUpserts.clear();
        this.optimisticDeletes.clear();
        this.isCommittingSyncTransactions = false;
        if (hasTruncateSync && truncateOptimisticSnapshot) {
          for (const [key, value] of truncateOptimisticSnapshot.upserts) {
            this.optimisticUpserts.set(key, value);
          }
          for (const key of truncateOptimisticSnapshot.deletes) {
            this.optimisticDeletes.add(key);
          }
        }
        for (const transaction of this.transactions.values()) {
          if (![`completed`, `failed`].includes(transaction.state)) {
            for (const mutation of transaction.mutations) {
              if (this.isThisCollection(mutation.collection) && mutation.optimistic) {
                switch (mutation.type) {
                  case `insert`:
                  case `update`:
                    this.optimisticUpserts.set(
                      mutation.key,
                      mutation.modified
                    );
                    this.optimisticDeletes.delete(mutation.key);
                    break;
                  case `delete`:
                    this.optimisticUpserts.delete(mutation.key);
                    this.optimisticDeletes.add(mutation.key);
                    break;
                }
              }
            }
          }
        }
        for (const key of changedKeys) {
          const previousVisibleValue = currentVisibleState.get(key);
          const newVisibleValue = this.get(key);
          const previousVirtualProps = this.getVirtualPropsSnapshotForState(key, {
            rowOrigins: previousRowOrigins,
            optimisticUpserts: previousOptimisticUpserts,
            optimisticDeletes: previousOptimisticDeletes,
            completedOptimisticKeys: completedOptimisticOps
          });
          const nextVirtualProps = this.getVirtualPropsSnapshotForState(key);
          const virtualChanged = previousVirtualProps.$synced !== nextVirtualProps.$synced || previousVirtualProps.$origin !== nextVirtualProps.$origin;
          const previousValueWithVirtual = previousVisibleValue !== void 0 ? virtualProps.enrichRowWithVirtualProps(
            previousVisibleValue,
            key,
            this.collection.id,
            () => previousVirtualProps.$synced,
            () => previousVirtualProps.$origin
          ) : void 0;
          const completedOp = completedOptimisticOps.get(key);
          let isRedundantSync = false;
          if (completedOp) {
            if (completedOp.type === `delete` && previousVisibleValue !== void 0 && newVisibleValue === void 0 && utils.deepEquals(completedOp.value, previousVisibleValue)) {
              isRedundantSync = true;
            } else if (newVisibleValue !== void 0 && utils.deepEquals(completedOp.value, newVisibleValue)) {
              isRedundantSync = true;
            }
          }
          const shouldEmitVirtualUpdate = virtualChanged && previousVisibleValue !== void 0 && newVisibleValue !== void 0 && utils.deepEquals(previousVisibleValue, newVisibleValue);
          if (isRedundantSync && !shouldEmitVirtualUpdate) {
            continue;
          }
          if (previousVisibleValue === void 0 && newVisibleValue !== void 0) {
            const completedOptimisticOp = completedOptimisticOps.get(key);
            if (completedOptimisticOp) {
              const previousValueFromCompleted = completedOptimisticOp.value;
              const previousValueWithVirtualFromCompleted = virtualProps.enrichRowWithVirtualProps(
                previousValueFromCompleted,
                key,
                this.collection.id,
                () => previousVirtualProps.$synced,
                () => previousVirtualProps.$origin
              );
              events.push({
                type: `update`,
                key,
                value: newVisibleValue,
                previousValue: previousValueWithVirtualFromCompleted
              });
            } else {
              events.push({
                type: `insert`,
                key,
                value: newVisibleValue
              });
            }
          } else if (previousVisibleValue !== void 0 && newVisibleValue === void 0) {
            events.push({
              type: `delete`,
              key,
              value: previousValueWithVirtual ?? previousVisibleValue
            });
          } else if (previousVisibleValue !== void 0 && newVisibleValue !== void 0 && (!utils.deepEquals(previousVisibleValue, newVisibleValue) || shouldEmitVirtualUpdate)) {
            events.push({
              type: `update`,
              key,
              value: newVisibleValue,
              previousValue: previousValueWithVirtual ?? previousVisibleValue
            });
          }
        }
        this.size = this.calculateSize();
        if (events.length > 0) {
          this.indexes.updateIndexes(events);
        }
        this.changes.emitEvents(events, true);
        this.pendingSyncedTransactions = uncommittedSyncedTransactions;
        this.preSyncVisibleState.clear();
        Promise.resolve().then(() => {
          this.recentlySyncedKeys.clear();
        });
        if (!this.hasReceivedFirstCommit) {
          this.hasReceivedFirstCommit = true;
        }
      }
    };
    this.config = config;
    this.transactions = new SortedMap.SortedMap(
      (a, b) => a.compareCreatedAt(b)
    );
    this.syncedData = new SortedMap.SortedMap(config.compare);
  }
  setDeps(deps) {
    this.collection = deps.collection;
    this.lifecycle = deps.lifecycle;
    this.changes = deps.changes;
    this.indexes = deps.indexes;
    this._events = deps.events;
  }
  /**
   * Checks if a row has pending optimistic mutations (not yet confirmed by sync).
   * Used to compute the $synced virtual property.
   */
  isRowSynced(key) {
    if (this.isLocalOnly) {
      return true;
    }
    return !this.optimisticUpserts.has(key) && !this.optimisticDeletes.has(key);
  }
  /**
   * Gets the origin of the last confirmed change to a row.
   * Returns 'local' if the row has optimistic mutations (optimistic changes are local).
   * Used to compute the $origin virtual property.
   */
  getRowOrigin(key) {
    if (this.isLocalOnly) {
      return "local";
    }
    if (this.optimisticUpserts.has(key) || this.optimisticDeletes.has(key)) {
      return "local";
    }
    return this.rowOrigins.get(key) ?? "remote";
  }
  createVirtualPropsSnapshot(key, overrides) {
    return {
      $synced: overrides?.$synced ?? this.isRowSynced(key),
      $origin: overrides?.$origin ?? this.getRowOrigin(key),
      $key: overrides?.$key ?? key,
      $collectionId: overrides?.$collectionId ?? this.collection.id
    };
  }
  getVirtualPropsSnapshotForState(key, options) {
    if (this.isLocalOnly) {
      return this.createVirtualPropsSnapshot(key, {
        $synced: true,
        $origin: "local"
      });
    }
    const optimisticUpserts = options?.optimisticUpserts ?? this.optimisticUpserts;
    const optimisticDeletes = options?.optimisticDeletes ?? this.optimisticDeletes;
    const hasOptimisticChange = optimisticUpserts.has(key) || optimisticDeletes.has(key) || options?.completedOptimisticKeys?.has(key) === true;
    return this.createVirtualPropsSnapshot(key, {
      $synced: !hasOptimisticChange,
      $origin: hasOptimisticChange ? "local" : (options?.rowOrigins ?? this.rowOrigins).get(key) ?? "remote"
    });
  }
  enrichWithVirtualPropsSnapshot(row, virtualProps2) {
    const existingRow = row;
    const synced = existingRow.$synced ?? virtualProps2.$synced;
    const origin = existingRow.$origin ?? virtualProps2.$origin;
    const resolvedKey = existingRow.$key ?? virtualProps2.$key;
    const collectionId = existingRow.$collectionId ?? virtualProps2.$collectionId;
    const cached = this.virtualPropsCache.get(row);
    if (cached && cached.synced === synced && cached.origin === origin && cached.key === resolvedKey && cached.collectionId === collectionId) {
      return cached.enriched;
    }
    const enriched = {
      ...row,
      $synced: synced,
      $origin: origin,
      $key: resolvedKey,
      $collectionId: collectionId
    };
    this.virtualPropsCache.set(row, {
      synced,
      origin,
      key: resolvedKey,
      collectionId,
      enriched
    });
    return enriched;
  }
  clearOriginTrackingState() {
    this.rowOrigins.clear();
    this.pendingLocalChanges.clear();
    this.pendingLocalOrigins.clear();
  }
  /**
   * Enriches a row with virtual properties using the "add-if-missing" pattern.
   * If the row already has virtual properties (from an upstream collection),
   * they are preserved. Otherwise, new values are computed.
   */
  enrichWithVirtualProps(row, key) {
    return this.enrichWithVirtualPropsSnapshot(
      row,
      this.createVirtualPropsSnapshot(key)
    );
  }
  /**
   * Creates a change message with virtual properties.
   * Uses the "add-if-missing" pattern so that pass-through from upstream
   * collections works correctly.
   */
  enrichChangeMessage(change) {
    const { __virtualProps } = change;
    const enrichedValue = __virtualProps?.value ? this.enrichWithVirtualPropsSnapshot(change.value, __virtualProps.value) : this.enrichWithVirtualProps(change.value, change.key);
    const enrichedPreviousValue = change.previousValue ? __virtualProps?.previousValue ? this.enrichWithVirtualPropsSnapshot(
      change.previousValue,
      __virtualProps.previousValue
    ) : this.enrichWithVirtualProps(change.previousValue, change.key) : void 0;
    return {
      key: change.key,
      type: change.type,
      value: enrichedValue,
      previousValue: enrichedPreviousValue,
      metadata: change.metadata
    };
  }
  /**
   * Get the current value for a key enriched with virtual properties.
   */
  getWithVirtualProps(key) {
    const value = this.get(key);
    if (value === void 0) {
      return void 0;
    }
    return this.enrichWithVirtualProps(value, key);
  }
  /**
   * Get the current value for a key (virtual derived state)
   */
  get(key) {
    const { optimisticDeletes, optimisticUpserts, syncedData } = this;
    if (optimisticDeletes.has(key)) {
      return void 0;
    }
    if (optimisticUpserts.has(key)) {
      return optimisticUpserts.get(key);
    }
    return syncedData.get(key);
  }
  /**
   * Check if a key exists in the collection (virtual derived state)
   */
  has(key) {
    const { optimisticDeletes, optimisticUpserts, syncedData } = this;
    if (optimisticDeletes.has(key)) {
      return false;
    }
    if (optimisticUpserts.has(key)) {
      return true;
    }
    return syncedData.has(key);
  }
  /**
   * Get all keys (virtual derived state)
   */
  *keys() {
    const { syncedData, optimisticDeletes, optimisticUpserts } = this;
    for (const key of syncedData.keys()) {
      if (!optimisticDeletes.has(key)) {
        yield key;
      }
    }
    for (const key of optimisticUpserts.keys()) {
      if (!syncedData.has(key) && !optimisticDeletes.has(key)) {
        yield key;
      }
    }
  }
  /**
   * Get all values (virtual derived state)
   */
  *values() {
    for (const key of this.keys()) {
      const value = this.get(key);
      if (value !== void 0) {
        yield value;
      }
    }
  }
  /**
   * Get all entries (virtual derived state)
   */
  *entries() {
    for (const key of this.keys()) {
      const value = this.get(key);
      if (value !== void 0) {
        yield [key, value];
      }
    }
  }
  /**
   * Get all entries (virtual derived state)
   */
  *[Symbol.iterator]() {
    for (const [key, value] of this.entries()) {
      yield [key, value];
    }
  }
  /**
   * Execute a callback for each entry in the collection
   */
  forEach(callbackfn) {
    let index = 0;
    for (const [key, value] of this.entries()) {
      callbackfn(value, key, index++);
    }
  }
  /**
   * Create a new array with the results of calling a function for each entry in the collection
   */
  map(callbackfn) {
    const result = [];
    let index = 0;
    for (const [key, value] of this.entries()) {
      result.push(callbackfn(value, key, index++));
    }
    return result;
  }
  /**
   * Check if the given collection is this collection
   * @param collection The collection to check
   * @returns True if the given collection is this collection, false otherwise
   */
  isThisCollection(collection) {
    return collection === this.collection;
  }
  /**
   * Recompute optimistic state from active transactions
   */
  recomputeOptimisticState(triggeredByUserAction = false) {
    if (this.isCommittingSyncTransactions && !triggeredByUserAction) {
      return;
    }
    const previousState = new Map(this.optimisticUpserts);
    const previousDeletes = new Set(this.optimisticDeletes);
    const previousRowOrigins = new Map(this.rowOrigins);
    for (const transaction of this.transactions.values()) {
      const isDirectTransaction = transaction.metadata[transactionMetadata.DIRECT_TRANSACTION_METADATA_KEY] === true;
      if (transaction.state === `completed`) {
        for (const mutation of transaction.mutations) {
          if (!this.isThisCollection(mutation.collection)) {
            continue;
          }
          this.pendingLocalOrigins.add(mutation.key);
          if (!mutation.optimistic) {
            continue;
          }
          switch (mutation.type) {
            case `insert`:
            case `update`:
              this.pendingOptimisticUpserts.set(
                mutation.key,
                mutation.modified
              );
              this.pendingOptimisticDeletes.delete(mutation.key);
              if (isDirectTransaction) {
                this.pendingOptimisticDirectUpserts.add(mutation.key);
                this.pendingOptimisticDirectDeletes.delete(mutation.key);
              } else {
                this.pendingOptimisticDirectUpserts.delete(mutation.key);
                this.pendingOptimisticDirectDeletes.delete(mutation.key);
              }
              break;
            case `delete`:
              this.pendingOptimisticUpserts.delete(mutation.key);
              this.pendingOptimisticDeletes.add(mutation.key);
              if (isDirectTransaction) {
                this.pendingOptimisticDirectUpserts.delete(mutation.key);
                this.pendingOptimisticDirectDeletes.add(mutation.key);
              } else {
                this.pendingOptimisticDirectUpserts.delete(mutation.key);
                this.pendingOptimisticDirectDeletes.delete(mutation.key);
              }
              break;
          }
        }
      } else if (transaction.state === `failed`) {
        for (const mutation of transaction.mutations) {
          if (!this.isThisCollection(mutation.collection)) {
            continue;
          }
          this.pendingLocalOrigins.delete(mutation.key);
          if (mutation.optimistic) {
            this.pendingOptimisticUpserts.delete(mutation.key);
            this.pendingOptimisticDeletes.delete(mutation.key);
            this.pendingOptimisticDirectUpserts.delete(mutation.key);
            this.pendingOptimisticDirectDeletes.delete(mutation.key);
          }
        }
      }
    }
    this.optimisticUpserts.clear();
    this.optimisticDeletes.clear();
    this.pendingLocalChanges.clear();
    const pendingSyncKeys = /* @__PURE__ */ new Set();
    for (const transaction of this.pendingSyncedTransactions) {
      for (const operation of transaction.operations) {
        pendingSyncKeys.add(operation.key);
      }
    }
    const staleOptimisticUpserts = [];
    for (const [key, value] of this.pendingOptimisticUpserts) {
      if (pendingSyncKeys.has(key) || this.pendingOptimisticDirectUpserts.has(key)) {
        this.optimisticUpserts.set(key, value);
      } else {
        staleOptimisticUpserts.push(key);
      }
    }
    for (const key of staleOptimisticUpserts) {
      this.pendingOptimisticUpserts.delete(key);
      this.pendingLocalOrigins.delete(key);
    }
    const staleOptimisticDeletes = [];
    for (const key of this.pendingOptimisticDeletes) {
      if (pendingSyncKeys.has(key) || this.pendingOptimisticDirectDeletes.has(key)) {
        this.optimisticDeletes.add(key);
      } else {
        staleOptimisticDeletes.push(key);
      }
    }
    for (const key of staleOptimisticDeletes) {
      this.pendingOptimisticDeletes.delete(key);
      this.pendingLocalOrigins.delete(key);
    }
    const activeTransactions = [];
    for (const transaction of this.transactions.values()) {
      if (![`completed`, `failed`].includes(transaction.state)) {
        activeTransactions.push(transaction);
      }
    }
    for (const transaction of activeTransactions) {
      for (const mutation of transaction.mutations) {
        if (!this.isThisCollection(mutation.collection)) {
          continue;
        }
        this.pendingLocalChanges.add(mutation.key);
        if (mutation.optimistic) {
          switch (mutation.type) {
            case `insert`:
            case `update`:
              this.optimisticUpserts.set(
                mutation.key,
                mutation.modified
              );
              this.optimisticDeletes.delete(mutation.key);
              break;
            case `delete`:
              this.optimisticUpserts.delete(mutation.key);
              this.optimisticDeletes.add(mutation.key);
              break;
          }
        }
      }
    }
    this.size = this.calculateSize();
    const events = [];
    this.collectOptimisticChanges(
      previousState,
      previousDeletes,
      previousRowOrigins,
      events
    );
    const filteredEventsBySyncStatus = events.filter((event) => {
      if (!this.recentlySyncedKeys.has(event.key)) {
        return true;
      }
      if (triggeredByUserAction) {
        return true;
      }
      return false;
    });
    if (this.pendingSyncedTransactions.length > 0 && !triggeredByUserAction) {
      const pendingSyncKeysForFilter = /* @__PURE__ */ new Set();
      for (const transaction of this.pendingSyncedTransactions) {
        for (const operation of transaction.operations) {
          pendingSyncKeysForFilter.add(operation.key);
        }
      }
      const filteredEvents = filteredEventsBySyncStatus.filter((event) => {
        if (event.type === `delete` && pendingSyncKeysForFilter.has(event.key)) {
          const hasActiveOptimisticMutation = activeTransactions.some(
            (tx) => tx.mutations.some(
              (m) => this.isThisCollection(m.collection) && m.key === event.key
            )
          );
          if (!hasActiveOptimisticMutation) {
            return false;
          }
        }
        return true;
      });
      if (filteredEvents.length > 0) {
        this.indexes.updateIndexes(filteredEvents);
      }
      this.changes.emitEvents(filteredEvents, triggeredByUserAction);
    } else {
      if (filteredEventsBySyncStatus.length > 0) {
        this.indexes.updateIndexes(filteredEventsBySyncStatus);
      }
      this.changes.emitEvents(filteredEventsBySyncStatus, triggeredByUserAction);
    }
  }
  /**
   * Calculate the current size based on synced data and optimistic changes
   */
  calculateSize() {
    const syncedSize = this.syncedData.size;
    const deletesFromSynced = Array.from(this.optimisticDeletes).filter(
      (key) => this.syncedData.has(key) && !this.optimisticUpserts.has(key)
    ).length;
    const upsertsNotInSynced = Array.from(this.optimisticUpserts.keys()).filter(
      (key) => !this.syncedData.has(key)
    ).length;
    return syncedSize - deletesFromSynced + upsertsNotInSynced;
  }
  /**
   * Collect events for optimistic changes
   */
  collectOptimisticChanges(previousUpserts, previousDeletes, previousRowOrigins, events) {
    const allKeys = /* @__PURE__ */ new Set([
      ...previousUpserts.keys(),
      ...this.optimisticUpserts.keys(),
      ...previousDeletes,
      ...this.optimisticDeletes
    ]);
    for (const key of allKeys) {
      const currentValue = this.get(key);
      const previousValue = this.getPreviousValue(
        key,
        previousUpserts,
        previousDeletes
      );
      const previousVirtualProps = this.getVirtualPropsSnapshotForState(key, {
        rowOrigins: previousRowOrigins,
        optimisticUpserts: previousUpserts,
        optimisticDeletes: previousDeletes
      });
      const nextVirtualProps = this.getVirtualPropsSnapshotForState(key);
      if (previousValue !== void 0 && currentValue === void 0) {
        events.push({
          type: `delete`,
          key,
          value: previousValue,
          __virtualProps: {
            value: previousVirtualProps
          }
        });
      } else if (previousValue === void 0 && currentValue !== void 0) {
        events.push({
          type: `insert`,
          key,
          value: currentValue,
          __virtualProps: {
            value: nextVirtualProps
          }
        });
      } else if (previousValue !== void 0 && currentValue !== void 0 && previousValue !== currentValue) {
        events.push({
          type: `update`,
          key,
          value: currentValue,
          previousValue,
          __virtualProps: {
            value: nextVirtualProps,
            previousValue: previousVirtualProps
          }
        });
      }
    }
  }
  /**
   * Get the previous value for a key given previous optimistic state
   */
  getPreviousValue(key, previousUpserts, previousDeletes) {
    if (previousDeletes.has(key)) {
      return void 0;
    }
    if (previousUpserts.has(key)) {
      return previousUpserts.get(key);
    }
    return this.syncedData.get(key);
  }
  /**
   * Schedule cleanup of a transaction when it completes
   */
  scheduleTransactionCleanup(transaction) {
    if (transaction.state === `completed`) {
      this.transactions.delete(transaction.id);
      return;
    }
    transaction.isPersisted.promise.then(() => {
      this.transactions.delete(transaction.id);
    }).catch(() => {
    });
  }
  /**
   * Capture visible state for keys that will be affected by pending sync operations
   * This must be called BEFORE onTransactionStateChange clears optimistic state
   */
  capturePreSyncVisibleState() {
    if (this.pendingSyncedTransactions.length === 0) return;
    const syncedKeys = /* @__PURE__ */ new Set();
    for (const transaction of this.pendingSyncedTransactions) {
      for (const operation of transaction.operations) {
        syncedKeys.add(operation.key);
      }
    }
    for (const key of syncedKeys) {
      this.recentlySyncedKeys.add(key);
    }
    for (const key of syncedKeys) {
      if (!this.preSyncVisibleState.has(key)) {
        const currentValue = this.get(key);
        if (currentValue !== void 0) {
          this.preSyncVisibleState.set(key, currentValue);
        }
      }
    }
  }
  /**
   * Trigger a recomputation when transactions change
   * This method should be called by the Transaction class when state changes
   */
  onTransactionStateChange() {
    this.changes.shouldBatchEvents = this.pendingSyncedTransactions.length > 0;
    this.capturePreSyncVisibleState();
    this.recomputeOptimisticState(false);
  }
  /**
   * Clean up the collection by stopping sync and clearing data
   * This can be called manually or automatically by garbage collection
   */
  cleanup() {
    this.syncedData.clear();
    this.syncedMetadata.clear();
    this.syncedCollectionMetadata.clear();
    this.optimisticUpserts.clear();
    this.optimisticDeletes.clear();
    this.pendingOptimisticUpserts.clear();
    this.pendingOptimisticDeletes.clear();
    this.pendingOptimisticDirectUpserts.clear();
    this.pendingOptimisticDirectDeletes.clear();
    this.clearOriginTrackingState();
    this.isLocalOnly = false;
    this.size = 0;
    this.pendingSyncedTransactions = [];
    this.syncedKeys.clear();
    this.hasReceivedFirstCommit = false;
  }
}
exports.CollectionStateManager = CollectionStateManager;
//# sourceMappingURL=state.cjs.map
