import type { StandardSchemaV1 } from '@standard-schema/spec';
import { type DeepMerge } from '../../../shared/src/deep-merge.ts';
import type { ReadonlyJSONValue } from '../../../shared/src/json.ts';
import type { BaseDefaultSchema, DefaultContext, DefaultSchema } from '../../../zero-types/src/default-types.ts';
import type { Schema } from '../../../zero-types/src/schema.ts';
import type { PullRow, Query } from './query.ts';
export type CustomQueryTypes<TTable extends keyof TSchema['tables'] & string, TInput extends ReadonlyJSONValue | undefined, TSchema extends Schema, TReturn, TContext> = 'Query' & {
    readonly $tableName: TTable;
    readonly $input: TInput;
    readonly $schema: TSchema;
    readonly $return: TReturn;
    readonly $context: TContext;
};
/**
 * CustomQuery is returned from defineQueries. It is a callable that captures
 * args and can be turned into a Query via {@link QueryRequest}.
 */
export type CustomQuery<TTable extends keyof TSchema['tables'] & string, TInput extends ReadonlyJSONValue | undefined, TOutput extends ReadonlyJSONValue | undefined = TInput, TSchema extends Schema = DefaultSchema, TReturn = PullRow<TTable, TSchema>, TContext = DefaultContext> = {
    readonly 'queryName': string;
    readonly 'fn': QueryDefinitionFunction<TTable, TInput, TReturn, TContext>;
    readonly '~': CustomQueryTypes<TTable, TInput, TSchema, TReturn, TContext>;
} & CustomQueryCallable<TTable, TInput, TOutput, TSchema, TReturn, TContext>;
type CustomQueryCallable<TTable extends keyof TSchema['tables'] & string, TInput extends ReadonlyJSONValue | undefined, TOutput extends ReadonlyJSONValue | undefined, TSchema extends Schema, TReturn, TContext> = [TInput] extends [undefined] ? () => QueryRequest<TTable, TInput, TOutput, TSchema, TReturn, TContext> : undefined extends TInput ? {
    (): QueryRequest<TTable, TInput, TOutput, TSchema, TReturn, TContext>;
    (args?: TInput): QueryRequest<TTable, TInput, TOutput, TSchema, TReturn, TContext>;
} : {
    (args: TInput): QueryRequest<TTable, TInput, TOutput, TSchema, TReturn, TContext>;
};
export type AnyCustomQuery = CustomQuery<string, any, any, Schema, any, any>;
export declare function isQuery<S extends Schema>(value: unknown): value is CustomQuery<string, any, any, S, any, any>;
export type QueryRequestTypes<TTable extends keyof TSchema['tables'] & string, TInput extends ReadonlyJSONValue | undefined, TOutput extends ReadonlyJSONValue | undefined, TSchema extends Schema, TReturn, TContext> = 'QueryRequest' & {
    readonly $tableName: TTable;
    readonly $input: TInput;
    readonly $output: TOutput;
    readonly $schema: TSchema;
    readonly $return: TReturn;
    readonly $context: TContext;
};
export type QueryRequest<TTable extends keyof TSchema['tables'] & string, TInput extends ReadonlyJSONValue | undefined, TOutput extends ReadonlyJSONValue | undefined, TSchema extends Schema, TReturn, TContext> = {
    readonly 'query': CustomQuery<TTable, TInput, TOutput, TSchema, TReturn, TContext>;
    readonly 'args': TInput;
    readonly '~': QueryRequestTypes<TTable, TInput, TOutput, TSchema, TReturn, TContext>;
};
/**
 * A shared type that can be a query request or a query builder.
 *
 * If it is a query request, it will be converted to a {@link Query} using the context.
 * Otherwise, it will be returned as is.
 */
export type QueryOrQueryRequest<TTable extends keyof TSchema['tables'] & string, TInput extends ReadonlyJSONValue | undefined, TOutput extends ReadonlyJSONValue | undefined, TSchema extends Schema, TReturn, TContext> = QueryRequest<TTable, TInput, TOutput, TSchema, TReturn, TContext> | Query<TTable, TSchema, TReturn>;
/**
 * Converts a query request to a {@link Query} using the context,
 * or returns the query as is.
 *
 * @param query - The query request or query builder to convert
 * @param context - The context to use to convert the query request
 */
export declare const addContextToQuery: <TTable extends keyof TSchema["tables"] & string, TInput extends ReadonlyJSONValue | undefined, TOutput extends ReadonlyJSONValue | undefined, TSchema extends Schema, TReturn, TContext>(query: QueryOrQueryRequest<TTable, TInput, TOutput, TSchema, TReturn, TContext>, context: TContext) => Query<TTable, TSchema, TReturn>;
export declare function isQueryRegistry(obj: unknown): obj is AnyQueryRegistry;
export type QueryRegistryTypes<TSchema extends Schema> = 'QueryRegistry' & {
    readonly $schema: TSchema;
};
export type QueryRegistry<QD extends QueryDefinitions, S extends Schema> = ToQueryTree<QD, S> & {
    ['~']: QueryRegistryTypes<S>;
};
export type AnyQueryRegistry = {
    ['~']: QueryRegistryTypes<Schema>;
    [key: string]: unknown;
};
type ToQueryTree<QD extends QueryDefinitions, S extends Schema> = {
    readonly [K in keyof QD]: QD[K] extends AnyQueryDefinition ? CustomQuery<QD[K]['~']['$tableName'], QD[K]['~']['$input'], QD[K]['~']['$output'], S, QD[K]['~']['$return'], QD[K]['~']['$context']> : QD[K] extends QueryDefinitions ? ToQueryTree<QD[K], S> : never;
};
export type FromQueryTree<QD extends QueryDefinitions, S extends Schema> = {
    readonly [K in keyof QD]: QD[K] extends AnyQueryDefinition ? CustomQuery<QD[K]['~']['$tableName'], ReadonlyJSONValue | undefined, ReadonlyJSONValue | undefined, S, QD[K]['~']['$return'], QD[K]['~']['$context']> : QD[K] extends QueryDefinitions ? FromQueryTree<QD[K], S> : never;
}[keyof QD];
export type QueryDefinitions = {
    readonly [key: string]: AnyQueryDefinition | QueryDefinitions;
};
export type QueryDefinitionTypes<TTable extends string, TInput extends ReadonlyJSONValue | undefined, TOutput, TReturn, TContext> = 'QueryDefinition' & {
    readonly $tableName: TTable;
    readonly $input: TInput;
    readonly $output: TOutput;
    readonly $return: TReturn;
    readonly $context: TContext;
};
/**
 * A query definition is the return type of `defineQuery()`.
 */
export type QueryDefinition<TTable extends string, TInput extends ReadonlyJSONValue | undefined, TOutput extends ReadonlyJSONValue | undefined, TReturn, TContext = DefaultContext> = {
    readonly 'fn': QueryDefinitionFunction<TTable, TOutput, TReturn, TContext>;
    readonly 'validator': StandardSchemaV1<TInput, TOutput> | undefined;
    readonly '~': QueryDefinitionTypes<TTable, TInput, TOutput, TReturn, TContext>;
};
export type AnyQueryDefinition = QueryDefinition<any, any, any, any, any>;
export declare function isQueryDefinition(f: unknown): f is AnyQueryDefinition;
export type QueryDefinitionFunction<TTable extends string, TInput extends ReadonlyJSONValue | undefined, TReturn, TContext> = (options: {
    args: TInput;
    ctx: TContext;
}) => Query<TTable, Schema, TReturn>;
/**
 * Defines a query to be used with {@link defineQueries}.
 *
 * The query function receives an object with `args` (the query arguments) and
 * `ctx` (the context). It should return a {@link Query} built using a builder
 * created from {@link createBuilder}.
 *
 * Note: A query defined with `defineQuery` must be passed to
 * {@link defineQueries} to be usable. The query name is derived from its
 * position in the `defineQueries` object.
 *
 * @example
 * ```ts
 * const builder = createBuilder(schema);
 *
 * const queries = defineQueries({
 *   // Simple query with no arguments
 *   allIssues: defineQuery(() => builder.issue.orderBy('created', 'desc')),
 *
 *   // Query with typed arguments
 *   issueById: defineQuery(({args}: {args: {id: string}}) =>
 *     builder.issue.where('id', args.id).one(),
 *   ),
 *
 *   // Query with validation using a Standard Schema validator (e.g., Zod)
 *   issuesByStatus: defineQuery(
 *     z.object({status: z.enum(['open', 'closed'])}),
 *     ({args}) => builder.issue.where('status', args.status),
 *   ),
 *
 *   // Query using context
 *   myIssues: defineQuery(({ctx}: {ctx: {userID: string}}) =>
 *     builder.issue.where('creatorID', ctx.userID),
 *   ),
 * });
 * ```
 *
 * @param queryFn - A function that receives `{args, ctx}` and returns a Query.
 * @returns A {@link QueryDefinition} that can be passed to {@link defineQueries}.
 *
 * @overload
 * @param validator - A Standard Schema validator for the arguments.
 * @param queryFn - A function that receives `{args, ctx}` and returns a Query.
 * @returns A {@link QueryDefinition} with validated arguments.
 */
export declare function defineQuery<TInput extends ReadonlyJSONValue | undefined, TContext = DefaultContext, TSchema extends Schema = DefaultSchema, TTable extends keyof TSchema['tables'] & string = keyof TSchema['tables'] & string, TReturn = PullRow<TTable, TSchema>>(queryFn: QueryDefinitionFunction<TTable, TInput, TReturn, TContext>): QueryDefinition<TTable, TInput, TInput, TReturn, TContext>;
export declare function defineQuery<TInput extends ReadonlyJSONValue | undefined, TOutput extends ReadonlyJSONValue | undefined, TContext = DefaultContext, TSchema extends Schema = DefaultSchema, TTable extends keyof TSchema['tables'] & string = keyof TSchema['tables'] & string, TReturn = PullRow<TTable, TSchema>>(validator: StandardSchemaV1<TInput, TOutput>, queryFn: QueryDefinitionFunction<TTable, TOutput, TReturn, TContext>): QueryDefinition<TTable, TInput, TOutput, TReturn, TContext>;
/**
 * Returns a typed version of {@link defineQuery} with the schema and context
 * types pre-specified. This enables better type inference when defining
 * queries.
 *
 * @example
 * ```ts
 * const zql = createBuilder(schema);
 *
 * // With both Schema and Context types
 * const defineAppQuery = defineQueryWithType<AppSchema, AppContext>();
 * const myQuery = defineAppQuery(({ctx}) =>
 *   zql.issue.where('userID', ctx.userID),
 * );
 *
 * // With just Context type (Schema inferred)
 * const defineAppQuery = defineQueryWithType<AppContext>();
 * ```
 *
 * @typeParam S - The Zero schema type.
 * @typeParam C - The context type passed to query functions.
 * @returns A function equivalent to {@link defineQuery} but with types
 *   pre-bound.
 */
export declare function defineQueryWithType<S extends Schema, C = unknown>(): TypedDefineQuery<S, C>;
/**
 * Returns a typed version of {@link defineQuery} with the context type
 * pre-specified.
 *
 * @typeParam C - The context type passed to query functions.
 * @returns A function equivalent to {@link defineQuery} but with the context
 *   type pre-bound.
 */
export declare function defineQueryWithType<C>(): TypedDefineQuery<Schema, C>;
/**
 * The return type of defineQueryWithType. A function matching the
 * defineQuery overloads but with Schema and Context pre-bound.
 */
type TypedDefineQuery<TSchema extends Schema, TContext> = {
    <TArgs extends ReadonlyJSONValue | undefined, TReturn, TTable extends keyof TSchema['tables'] & string = keyof TSchema['tables'] & string>(queryFn: QueryDefinitionFunction<TTable, TArgs, TReturn, TContext>): QueryDefinition<TTable, TArgs, TArgs, TReturn, TContext>;
    <TInput extends ReadonlyJSONValue | undefined, TOutput extends ReadonlyJSONValue | undefined, TReturn, TTable extends keyof TSchema['tables'] & string = keyof TSchema['tables'] & string>(validator: StandardSchemaV1<TInput, TOutput>, queryFn: QueryDefinitionFunction<TTable, TOutput, TReturn, TContext>): QueryDefinition<TTable, TInput, TOutput, TReturn, TContext>;
};
export declare function createQuery<TTable extends keyof TSchema['tables'] & string, TInput extends ReadonlyJSONValue | undefined, TOutput extends ReadonlyJSONValue | undefined, TSchema extends Schema, TReturn, TContext>(name: string, definition: QueryDefinition<TTable, TInput, TOutput, TReturn, TContext>): CustomQuery<TTable, TInput, TOutput, TSchema, TReturn, TContext>;
/**
 * Converts query definitions created with {@link defineQuery} into callable
 * {@link Query} objects that can be invoked with arguments and a context.
 *
 * Query definitions can be nested for organization. The resulting query names
 * are dot-separated paths (e.g., `users.byId`).
 *
 * @example
 * ```ts
 * const builder = createBuilder(schema);
 *
 * const queries = defineQueries({
 *   issues: defineQuery(() => builder.issue.orderBy('created', 'desc')),
 *   users: {
 *     byId: defineQuery(({args}: {args: {id: string}}) =>
 *       builder.user.where('id', args.id),
 *     ),
 *   },
 * });
 *
 * // Usage:
 * const request = queries.issues.byId({id: '123'});
 * const [data] = zero.useQuery(request);
 * ```
 *
 * @param defs - An object containing query definitions or nested objects of
 *   query definitions.
 * @returns An object with the same structure where each query definition is
 *   converted to a {@link CustomQuery}.
 */
export declare function defineQueries<const QD, S extends BaseDefaultSchema = DefaultSchema>(defs: QD & AssertQueryDefinitions<QD>): QueryRegistry<EnsureQueryDefinitions<QD>, S>;
export declare function defineQueries<const TBase, const TOverrides, S extends BaseDefaultSchema = DefaultSchema>(base: QueryRegistry<EnsureQueryDefinitions<TBase>, S> | (TBase & AssertQueryDefinitions<TBase>), overrides: TOverrides & AssertQueryDefinitions<TOverrides>): QueryRegistry<DeepMerge<EnsureQueryDefinitions<TBase>, EnsureQueryDefinitions<TOverrides>, AnyQueryDefinition>, S>;
export type AssertQueryDefinitions<QD> = QD extends QueryDefinitions ? unknown : never;
export type EnsureQueryDefinitions<QD> = QD extends QueryDefinitions ? QD : QD extends QueryRegistry<infer InnerQD, infer _S> ? InnerQD : never;
/**
 * Creates a function that can be used to define queries with a specific schema.
 */
export declare function defineQueriesWithType<TSchema extends Schema>(): TypedDefineQueries<TSchema>;
/**
 * The return type of defineQueriesWithType. A function matching the
 * defineQueries overloads but with Schema pre-bound.
 */
type TypedDefineQueries<S extends Schema> = {
    <QD>(definitions: QD & AssertQueryDefinitions<QD>): QueryRegistry<EnsureQueryDefinitions<QD>, S>;
    <TBase, TOverrides>(base: QueryRegistry<EnsureQueryDefinitions<TBase>, S> | (TBase & AssertQueryDefinitions<TBase>), overrides: TOverrides & AssertQueryDefinitions<TOverrides>): QueryRegistry<DeepMerge<EnsureQueryDefinitions<TBase>, EnsureQueryDefinitions<TOverrides>, AnyQueryDefinition>, S>;
};
export declare function getQuery<QD extends QueryDefinitions, S extends Schema>(queries: QueryRegistry<QD, S>, name: string): FromQueryTree<QD, S> | undefined;
export declare function mustGetQuery<QD extends QueryDefinitions, S extends Schema>(queries: QueryRegistry<QD, S>, name: string): FromQueryTree<QD, S>;
export {};
//# sourceMappingURL=query-registry.d.ts.map