import { reconcile, produce } from "solid-js/store";
import { emptyArray } from "../../shared/src/sentinels.js";
import { skipYields } from "../../zql/src/ivm/operator.js";
import { idSymbol, applyChange } from "../../zql/src/ivm/view-apply-change.js";
import "../../zero-protocol/src/ast.js";
import "js-xxhash";
import "../../shared/src/config.js";
import "@rocicorp/resolver";
import "../../shared/src/resolved-promises.js";
import "../../shared/src/sleep.js";
import "../../replicache/src/hash.js";
import "../../replicache/src/deleted-clients.js";
import "@rocicorp/lock";
import "@rocicorp/logger";
import "../../replicache/src/persist/client-groups.js";
import "../../replicache/src/persist/clients.js";
import "../../shared/src/json-schema.js";
import "../../zero-protocol/src/custom-queries.js";
import "../../zero-protocol/src/client-schema.js";
import "../../zero-schema/src/permissions.js";
import "../../shared/src/document-visible.js";
import "../../shared/src/iterables.js";
import "../../replicache/src/btree/node.js";
import "compare-utf8";
import "../../replicache/src/sync/push.js";
import "../../shared/src/valita.js";
import "../../zero-protocol/src/connect.js";
import "../../zero-protocol/src/down.js";
import "../../zero-protocol/src/error.js";
import "../../zero-protocol/src/protocol-version.js";
import "../../zero-protocol/src/push.js";
import "../../zero-protocol/src/version.js";
import "../../shared/src/btree-set.js";
import "../../zero-protocol/src/primary-key.js";
import "../../datadog/src/datadog-log-sink.js";
import "../../zero-client/src/client/reload-error-handler.js";
const COMPLETE = Object.freeze({ type: "complete" });
const UNKNOWN = Object.freeze({ type: "unknown" });
class SolidView {
  #input;
  #format;
  #onDestroy;
  #retry;
  #setState;
  // Optimization: if the store is currently empty we build up
  // the view on a plain old JS object stored at #builderRoot, and return
  // that for the new state on transaction commit.  This avoids building up
  // large views from scratch via solid produce.  The proxy object used by
  // solid produce is slow and in this case we don't care about solid tracking
  // the fine grained changes (everything has changed, it's all new).  For a
  // test case with a view with 3000 rows, each row having 2 children, this
  // optimization reduced #applyChanges time from 743ms to 133ms.
  #builderRoot;
  #pendingChanges = [];
  #updateTTL;
  constructor(input, onTransactionCommit, format, onDestroy, queryComplete, updateTTL, setState, retry) {
    this.#input = input;
    onTransactionCommit(this.#onTransactionCommit);
    this.#format = format;
    this.#onDestroy = onDestroy;
    this.#updateTTL = updateTTL;
    this.#retry = retry;
    input.setOutput(this);
    const initialRoot = this.#createEmptyRoot();
    this.#applyChangesToRoot(
      skipYields(input.fetch({})),
      (node) => ({ type: "add", node }),
      initialRoot
    );
    this.#setState = setState;
    this.#setState(
      reconcile(
        [
          initialRoot,
          queryComplete === true ? COMPLETE : "error" in queryComplete ? this.#makeError(queryComplete) : UNKNOWN
        ],
        {
          // solidjs's types want a string, but a symbol works
          key: idSymbol
        }
      )
    );
    if (isEmptyRoot(initialRoot)) {
      this.#builderRoot = this.#createEmptyRoot();
    }
    if (queryComplete !== true && !("error" in queryComplete)) {
      void queryComplete.then(() => {
        this.#setState((prev) => [prev[0], COMPLETE]);
      }).catch((error) => {
        this.#setState((prev) => [prev[0], this.#makeError(error)]);
      });
    }
  }
  #makeError(error) {
    const message = error.message ?? "An unknown error occurred";
    return {
      type: "error",
      retry: this.#retry,
      refetch: this.#retry,
      error: {
        type: error.error,
        message,
        ...error.details ? { details: error.details } : {}
      }
    };
  }
  destroy() {
    this.#onDestroy();
  }
  #onTransactionCommit = () => {
    const builderRoot = this.#builderRoot;
    if (builderRoot) {
      if (!isEmptyRoot(builderRoot)) {
        this.#setState(
          0,
          reconcile(builderRoot, {
            // solidjs's types want a string, but a symbol works
            key: idSymbol
          })
        );
        this.#setState((prev) => [builderRoot, prev[1]]);
        this.#builderRoot = void 0;
      }
    } else {
      try {
        this.#applyChanges(this.#pendingChanges, (c) => c);
      } finally {
        this.#pendingChanges = [];
      }
    }
  };
  push(change) {
    if (this.#builderRoot) {
      this.#applyChangeToRoot(change, this.#builderRoot);
    } else {
      this.#pendingChanges.push(materializeRelationships(change));
    }
    return emptyArray;
  }
  #applyChanges(changes, mapper) {
    this.#setState(
      produce((draftState) => {
        this.#applyChangesToRoot(changes, mapper, draftState[0]);
        if (isEmptyRoot(draftState[0])) {
          this.#builderRoot = this.#createEmptyRoot();
        }
      })
    );
  }
  #applyChangesToRoot(changes, mapper, root) {
    for (const change of changes) {
      this.#applyChangeToRoot(mapper(change), root);
    }
  }
  #applyChangeToRoot(change, root) {
    applyChange(
      root,
      change,
      this.#input.getSchema(),
      "",
      this.#format,
      true
    );
  }
  #createEmptyRoot() {
    return {
      "": this.#format.singular ? void 0 : []
    };
  }
  updateTTL(ttl) {
    this.#updateTTL(ttl);
  }
}
function materializeRelationships(change) {
  switch (change.type) {
    case "add":
      return { type: "add", node: materializeNodeRelationships(change.node) };
    case "remove":
      return { type: "remove", node: materializeNodeRelationships(change.node) };
    case "child":
      return {
        type: "child",
        node: { row: change.node.row },
        child: {
          relationshipName: change.child.relationshipName,
          change: materializeRelationships(change.child.change)
        }
      };
    case "edit":
      return {
        type: "edit",
        node: { row: change.node.row },
        oldNode: { row: change.oldNode.row }
      };
  }
}
function materializeNodeRelationships(node) {
  const relationships = {};
  for (const relationship in node.relationships) {
    const materialized = [];
    for (const n of skipYields(node.relationships[relationship]())) {
      materialized.push(materializeNodeRelationships(n));
    }
    relationships[relationship] = () => materialized;
  }
  return {
    row: node.row,
    relationships
  };
}
function isEmptyRoot(entry) {
  const data = entry[""];
  return data === void 0 || Array.isArray(data) && data.length === 0;
}
function createSolidViewFactory(setState, retry) {
  function solidViewFactory(_query, input, format, onDestroy, onTransactionCommit, queryComplete, updateTTL) {
    return new SolidView(
      input,
      onTransactionCommit,
      format,
      onDestroy,
      queryComplete,
      updateTTL,
      setState,
      retry || (() => {
      })
    );
  }
  return solidViewFactory;
}
export {
  COMPLETE,
  SolidView,
  UNKNOWN,
  createSolidViewFactory
};
//# sourceMappingURL=solid-view.js.map
