import { deepMerge, isPlainObject } from "../../../shared/src/deep-merge.js";
import { must } from "../../../shared/src/must.js";
import { getValueAtPath } from "../../../shared/src/object-traversal.js";
import { asQueryInternals } from "./query-internals.js";
import { validateInput } from "./validate-input.js";
function isQuery(value) {
  return typeof value === "function" && typeof value.queryName === "string" && typeof value.fn === "function";
}
const addContextToQuery = (query, context) => "query" in query ? query.query.fn({ ctx: context, args: query.args }) : query;
function isQueryRegistry(obj) {
  return typeof obj === "object" && obj !== null && obj?.["~"] === "QueryRegistry";
}
function isQueryDefinition(f) {
  return typeof f === "object" && f !== null && f["~"] === "QueryDefinition";
}
function defineQuery(validatorOrQueryFn, queryFn) {
  let validator;
  let actualQueryFn;
  if ("~standard" in validatorOrQueryFn) {
    validator = validatorOrQueryFn;
    actualQueryFn = must(queryFn);
  } else {
    validator = void 0;
    actualQueryFn = validatorOrQueryFn;
  }
  const queryDefinition = {
    "fn": actualQueryFn,
    "validator": validator,
    "~": "QueryDefinition"
  };
  return queryDefinition;
}
function defineQueryWithType() {
  return defineQuery;
}
function createQuery(name, definition) {
  const { validator } = definition;
  const fn = (options) => {
    const validatedArgs = validator ? validateInput(name, options.args, validator, "query") : options.args;
    return asQueryInternals(
      definition.fn({
        args: validatedArgs,
        ctx: options.ctx
      })
    ).nameAndArgs(
      name,
      // TODO(arv): Get rid of the array?
      // Send original input args to server (not transformed output)
      options.args === void 0 ? [] : [options.args]
    );
  };
  const query = (args) => ({
    args,
    "~": "QueryRequest",
    "query": query
  });
  query.queryName = name;
  query.fn = fn;
  query["~"] = "Query";
  return query;
}
function defineQueries(defsOrBase, overrides) {
  function processDefinitions(definitions, path) {
    const result = {
      ["~"]: "QueryRegistry"
    };
    for (const [key, value] of Object.entries(definitions)) {
      path.push(key);
      const defaultName = path.join(".");
      if (isQueryDefinition(value)) {
        result[key] = createQuery(defaultName, value);
      } else {
        result[key] = processDefinitions(value, path);
      }
      path.pop();
    }
    return result;
  }
  if (overrides !== void 0) {
    let base;
    if (!isQueryRegistry(defsOrBase)) {
      base = processDefinitions(defsOrBase, []);
    } else {
      base = defsOrBase;
    }
    const processed = processDefinitions(overrides, []);
    const merged = deepMerge(base, processed, isQueryLeaf);
    merged["~"] = "QueryRegistry";
    return merged;
  }
  return processDefinitions(defsOrBase, []);
}
const isQueryLeaf = (value) => !isPlainObject(value) || isQuery(value);
function defineQueriesWithType() {
  return defineQueries;
}
function getQuery(queries, name) {
  const q = getValueAtPath(queries, name, /[.|]/);
  return q;
}
function mustGetQuery(queries, name) {
  const query = getQuery(queries, name);
  if (query === void 0) {
    throw new Error(`Query not found: ${name}`);
  }
  return query;
}
export {
  addContextToQuery,
  createQuery,
  defineQueries,
  defineQueriesWithType,
  defineQuery,
  defineQueryWithType,
  getQuery,
  isQuery,
  isQueryDefinition,
  isQueryRegistry,
  mustGetQuery
};
//# sourceMappingURL=query-registry.js.map
