import { resolver } from "@rocicorp/resolver";
import { hashOfNameAndArgs } from "../../../zero-protocol/src/query-hash.js";
import { buildPipeline } from "../builder/builder.js";
import { ArrayView } from "../ivm/array-view.js";
import { MemoryStorage } from "../ivm/memory-storage.js";
import { asQueryInternals } from "./query-internals.js";
import { DEFAULT_TTL_MS, DEFAULT_PRELOAD_TTL_MS } from "./ttl.js";
class QueryDelegateBase {
  /**
   * Default implementation that just calls applyViewUpdates synchronously.
   * Override if you need custom batching behavior (e.g., SolidJS).
   */
  batchViewUpdates(applyViewUpdates) {
    return applyViewUpdates();
  }
  /**
   * Default implementation returns MemoryStorage.
   * Override if you need custom storage.
   */
  createStorage() {
    return new MemoryStorage();
  }
  materialize(query, factory, options) {
    return materializeImpl(query, this, factory, options);
  }
  /**
   * Default implementation calls runImpl.
   * Override if you need custom query execution (e.g., TestPGQueryDelegate).
   */
  run(query, options) {
    return runImpl(query, this, options);
  }
  /**
   * Default implementation calls preloadImpl.
   * Override if you need custom preload behavior.
   */
  preload(query, options) {
    return preloadImpl(query, this, options);
  }
  /**
   * Default no-op implementation for decorateSourceInput.
   * Override if you need to wrap or instrument source inputs.
   */
  decorateSourceInput(input, _queryID) {
    return input;
  }
  /**
   * Default no-op implementation for decorateInput.
   * Override if you need to wrap or instrument inputs.
   */
  decorateInput(input, _name) {
    return input;
  }
  /**
   * Default no-op implementation for decorateFilterInput.
   * Override if you need to wrap or instrument filter inputs.
   */
  decorateFilterInput(input, _name) {
    return input;
  }
  /**
   * Default no-op implementation for addEdge.
   * Override if you need to track graph edges (e.g., visualization).
   */
  addEdge(_source, _dest) {
  }
  /**
   * Default no-op implementation for addMetric.
   * Override if you need to collect metrics.
   */
  addMetric(_metric, _value, ..._args) {
  }
  /**
   * Default no-op implementation.
   * Override if you need to track server queries (e.g., ZeroContext, test delegates).
   */
  addServerQuery(_ast, _ttl, _gotCallback) {
    return () => {
    };
  }
  /**
   * Default no-op implementation.
   * Override if you need to track custom queries (e.g., ZeroContext, test delegates).
   */
  addCustomQuery(_ast, _customQueryID, _ttl, _gotCallback) {
    return () => {
    };
  }
  /**
   * Default no-op implementation.
   * Override if you need to handle query updates.
   */
  updateServerQuery(_ast, _ttl) {
  }
  /**
   * Default no-op implementation.
   * Override if you need to handle custom query updates.
   */
  updateCustomQuery(_customQueryID, _ttl) {
  }
  /**
   * Default no-op implementation.
   * Override if you need to flush query changes.
   */
  flushQueryChanges() {
  }
  /**
   * Called when a transaction commits. Override to add custom behavior.
   * Default implementation returns a no-op cleanup function.
   */
  onTransactionCommit(_cb) {
    return () => {
    };
  }
  /**
   * Validates run options. Override to add custom validation.
   * Default implementation is a no-op.
   */
  assertValidRunOptions(_options) {
  }
}
async function runImpl(query, delegate, options) {
  delegate.assertValidRunOptions(options);
  const v = materializeImpl(
    query,
    delegate,
    void 0,
    {
      ttl: options?.ttl
    }
  );
  if (options?.type === "complete") {
    return new Promise((resolve) => {
      v.addListener((data, type) => {
        if (type === "complete") {
          v.destroy();
          resolve(data);
        } else if (type === "error") {
          v.destroy();
          resolve(Promise.reject(data));
        }
      });
    });
  }
  options?.type;
  const ret = v.data;
  v.destroy();
  return ret;
}
function preloadImpl(query, delegate, options) {
  const qi = asQueryInternals(query);
  const ttl = options?.ttl ?? DEFAULT_PRELOAD_TTL_MS;
  const { resolve, promise: complete } = resolver();
  const { customQueryID, ast } = qi;
  if (customQueryID) {
    const cleanup2 = delegate.addCustomQuery(ast, customQueryID, ttl, (got) => {
      if (got) {
        resolve();
      }
    });
    return {
      cleanup: cleanup2,
      complete
    };
  }
  const cleanup = delegate.addServerQuery(ast, ttl, (got) => {
    if (got) {
      resolve();
    }
  });
  return {
    cleanup,
    complete
  };
}
function materializeImpl(query, delegate, factory = arrayViewFactory, options) {
  let ttl = options?.ttl ?? DEFAULT_TTL_MS;
  const qi = asQueryInternals(query);
  const { ast, format, customQueryID } = qi;
  const queryHash = qi.hash();
  const queryID = customQueryID ? hashOfNameAndArgs(customQueryID.name, customQueryID.args) : queryHash;
  const queryCompleteResolver = resolver();
  let queryComplete = delegate.defaultQueryComplete;
  const updateTTL = customQueryID ? (newTTL) => delegate.updateCustomQuery(customQueryID, newTTL) : (newTTL) => delegate.updateServerQuery(ast, newTTL);
  const gotCallback = (got, error) => {
    if (error) {
      queryCompleteResolver.reject(error);
      queryComplete = error;
      return;
    }
    if (got) {
      delegate.addMetric(
        "query-materialization-end-to-end",
        performance.now() - t0,
        queryID,
        ast
      );
      queryComplete = true;
      queryCompleteResolver.resolve(true);
    }
  };
  let removeCommitObserver;
  const onDestroy = () => {
    input.destroy();
    removeCommitObserver?.();
    removeAddedQuery();
  };
  const t0 = performance.now();
  const removeAddedQuery = customQueryID ? delegate.addCustomQuery(ast, customQueryID, ttl, gotCallback) : delegate.addServerQuery(ast, ttl, gotCallback);
  const input = buildPipeline(ast, delegate, queryID);
  const view = delegate.batchViewUpdates(
    () => (factory ?? arrayViewFactory)(
      query,
      input,
      format,
      onDestroy,
      (cb) => {
        removeCommitObserver = delegate.onTransactionCommit(cb);
      },
      queryComplete || queryCompleteResolver.promise,
      updateTTL
    )
  );
  delegate.addMetric(
    "query-materialization-client",
    performance.now() - t0,
    queryID
  );
  return view;
}
function arrayViewFactory(_query, input, format, onDestroy, onTransactionCommit, queryComplete, updateTTL) {
  const v = new ArrayView(
    input,
    format,
    queryComplete,
    updateTTL
  );
  v.onDestroy = onDestroy;
  onTransactionCommit(() => {
    v.flush();
  });
  return v;
}
export {
  QueryDelegateBase,
  materializeImpl,
  preloadImpl,
  runImpl
};
//# sourceMappingURL=query-delegate-base.js.map
