import { BooleanFlag } from '@oclif/core/interfaces';
import { CustomOptions } from '@oclif/core/interfaces';
import { DefinedTelemetryTrace } from '@sanity/telemetry';
import { ExprNode } from 'groq-js';
import { FSWatcher } from 'chokidar';
import { OptionFlag } from '@oclif/core/interfaces';
import { SanityCommand } from '@sanity/cli-core';
import { SchemaType } from 'groq-js';
import { spinner } from '@sanity/cli-core/ux';
import * as t from '@babel/types';
import { TransformOptions } from '@babel/core';
import { WorkerChannel } from '@sanity/worker-channels';
import { WorkerChannelReporter } from '@sanity/worker-channels';
import * as z from 'zod';

/**
 * @deprecated use TypeGenConfig
 */
export declare type CodegenConfig = TypeGenConfig;

/**
 * @internal
 */
export declare const configDefinition: z.ZodObject<{
    formatGeneratedCode: z.ZodDefault<z.ZodBoolean>;
    generates: z.ZodDefault<z.ZodString>;
    overloadClientMethods: z.ZodDefault<z.ZodBoolean>;
    path: z.ZodDefault<z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString>]>>;
    schema: z.ZodDefault<z.ZodString>;
}, z.core.$strip>;

/**
 * A module containing queries that have been evaluated.
 * @public
 */
export declare interface EvaluatedModule {
    errors: (QueryEvaluationError | QueryExtractionError)[];
    filename: string;
    queries: EvaluatedQuery[];
}

/**
 * An `ExtractedQuery` that has been evaluated against a schema, yielding a TypeScript type.
 * @public
 */
export declare interface EvaluatedQuery extends ExtractedQuery {
    ast: t.ExportNamedDeclaration;
    code: string;
    id: t.Identifier;
    stats: TypeEvaluationStats;
    tsType: t.TSType;
}

/**
 * A module (file) containing extracted GROQ queries.
 * @public
 */
export declare interface ExtractedModule {
    errors: QueryExtractionError[];
    filename: string;
    queries: ExtractedQuery[];
}

/**
 * A GROQ query extracted from a source file.
 * @public
 */
export declare interface ExtractedQuery {
    filename: string;
    query: string;
    variable: QueryVariable;
}

/**
 * Filter a union of object types by the _type property. This is handy when working with page builder
 * setups where the returned type is an array containing multiple types.
 *
 * @example
 * ```ts
 *
 * export type Callout = {
 *   _type: 'callout'
 *   title?: string
 *   content?: string
 * }
 *
 * export type Video = {
 *   _type: 'video'
 *   url?: string
 *   caption?: string
 * }
 * type FORM_QUERY_RESULT = {
 *   _id: string
 *   title?: string
 *   content?: Array<
 *     | ({ _key: string } & Callout)
 *     | ({ _key: string } & Video)
 *   >
 * } | null
 *
 * // Get the type of the content with the Get helper
 * type Content = Get<FORM_QUERY_RESULT, 'content', number>
 *
 * // Get the type for a callout module from the page builder type
 * type CalloutModule = FilterByType<Content, 'callout'>
 * // → { _key: string } & Callout
 * ```
 */
export declare type FilterByType<U extends {
    _type: string;
}, T extends U['_type']> = Extract<U, {
    _type: T;
}>;

/**
 * findQueriesInPath takes a path or array of paths and returns all GROQ queries in the files.
 * @param path - The path or array of paths to search for queries
 * @param babelOptions - The babel configuration to use when parsing the source
 * @param resolver - A resolver function to use when resolving module imports
 * @returns An async generator that yields the results of the search
 * @beta
 * @internal
 */
export declare function findQueriesInPath({ babelOptions, path, resolver, }: FindQueriesInPathOptions): {
    files: string[];
    queries: AsyncIterable<ExtractedModule>;
};

declare interface FindQueriesInPathOptions {
    path: string | string[];
    babelOptions?: TransformOptions;
    resolver?: NodeJS.RequireResolve;
}

/**
 * findQueriesInSource takes a source string and returns all GROQ queries in it.
 * @param source - The source code to search for queries
 * @param filename - The filename of the source code
 * @param babelConfig - The babel configuration to use when parsing the source
 * @param resolver - A resolver function to use when resolving module imports
 * @returns
 * @beta
 * @internal
 */
export declare function findQueriesInSource(source: string, filename: string, babelConfig?: TransformOptions, resolver?: NodeJS.RequireResolve): ExtractedModule;

export declare interface GenerateTypesOptions {
    schema: SchemaType;
    overloadClientMethods?: boolean;
    queries?: AsyncIterable<ExtractedModule>;
    reporter?: WorkerChannelReporter<TypegenWorkerChannel>;
    root?: string;
    schemaPath?: string;
}

/**
 * Result from a single generation run.
 * @internal
 */
export declare interface GenerationResult {
    code: string;
    duration: number;
    emptyUnionTypeNodesGenerated: number;
    filesWithErrors: number;
    outputSize: number;
    queriesCount: number;
    queryFilesCount: number;
    schemaTypesCount: number;
    typeNodesGenerated: number;
    unknownTypeNodesGenerated: number;
    unknownTypeNodesRatio: number;
}

/**
 * Get a deeply nested property type from a complex type structure. Safely navigates
 * through nullable types (`T | null | undefined`) at each level, preserving the
 * nullability of the final accessed property.
 *
 * Supports up to 20 levels of nesting.
 *
 * @example
 * ```ts
 * type POST_QUERY_RESULT = {
 *   _id: string
 *   author: {
 *     profile: {
 *       name: string;
 *     } | null;
 *   } | null;
 *   links: Array<{
 *     _key: string
 *     type: 'link'
 *     label: string
 *     url: string
 *   }> | null
 * } | null
 *
 * // Basic property access:
 * type Id = Get<POST_QUERY_RESULT, '_id'>;
 * // → string
 *
 * // Nested property access:
 * type Profile = Get<POST_QUERY_RESULT, 'author', 'profile';
 * // → { name: string } | null
 *
 * // Array element access using `number`:
 * type Link = Get<POST_QUERY_RESULT, 'links', number, 'label'>;
 * // → string
 * ```
 */
export declare type Get<T, K1 extends keyof NonNullish<T>, K2 extends keyof NavigatePath<T, [K1]> = never, K3 extends keyof NavigatePath<T, [K1, K2]> = never, K4 extends keyof NavigatePath<T, [K1, K2, K3]> = never, K5 extends keyof NavigatePath<T, [K1, K2, K3, K4]> = never, K6 extends keyof NavigatePath<T, [K1, K2, K3, K4, K5]> = never, K7 extends keyof NavigatePath<T, [K1, K2, K3, K4, K5, K6]> = never, K8 extends keyof NavigatePath<T, [K1, K2, K3, K4, K5, K6, K7]> = never, K9 extends keyof NavigatePath<T, [K1, K2, K3, K4, K5, K6, K7, K8]> = never, K10 extends keyof NavigatePath<T, [K1, K2, K3, K4, K5, K6, K7, K8, K9]> = never, K11 extends keyof NavigatePath<T, [K1, K2, K3, K4, K5, K6, K7, K8, K9, K10]> = never, K12 extends keyof NavigatePath<T, [K1, K2, K3, K4, K5, K6, K7, K8, K9, K10, K11]> = never, K13 extends keyof NavigatePath<T, [K1, K2, K3, K4, K5, K6, K7, K8, K9, K10, K11, K12]> = never, K14 extends keyof NavigatePath<T, [K1, K2, K3, K4, K5, K6, K7, K8, K9, K10, K11, K12, K13]> = never, K15 extends keyof NavigatePath<T, [K1, K2, K3, K4, K5, K6, K7, K8, K9, K10, K11, K12, K13, K14]> = never, K16 extends keyof NavigatePath<T, [
K1,
K2,
K3,
K4,
K5,
K6,
K7,
K8,
K9,
K10,
K11,
K12,
K13,
K14,
K15
]> = never, K17 extends keyof NavigatePath<T, [
K1,
K2,
K3,
K4,
K5,
K6,
K7,
K8,
K9,
K10,
K11,
K12,
K13,
K14,
K15,
K16
]> = never, K18 extends keyof NavigatePath<T, [
K1,
K2,
K3,
K4,
K5,
K6,
K7,
K8,
K9,
K10,
K11,
K12,
K13,
K14,
K15,
K16,
K17
]> = never, K19 extends keyof NavigatePath<T, [
K1,
K2,
K3,
K4,
K5,
K6,
K7,
K8,
K9,
K10,
K11,
K12,
K13,
K14,
K15,
K16,
K17,
K18
]> = never, K20 extends keyof NavigatePath<T, [
K1,
K2,
K3,
K4,
K5,
K6,
K7,
K8,
K9,
K10,
K11,
K12,
K13,
K14,
K15,
K16,
K17,
K18,
K19
]> = never> = GetAtPath<T, TakeUntilNever<[
K1,
K2,
K3,
K4,
K5,
K6,
K7,
K8,
K9,
K10,
K11,
K12,
K13,
K14,
K15,
K16,
K17,
K18,
K19,
K20
]>>;

/** Recursively gets value at path, preserving nullability at final access. */
declare type GetAtPath<T, Path extends unknown[]> = Path extends [] ? T : Path extends [infer K] ? K extends keyof NonNullish<T> ? NonNullish<T>[K] : never : Path extends [infer K, ...infer Rest] ? K extends keyof NonNullish<T> ? GetAtPath<NonNullish<T>[K], Rest> : never : never;

/**
 * This is a custom implementation of require.resolve that takes into account the paths
 * configuration in tsconfig.json. This is necessary if we want to resolve paths that are
 * custom defined in the tsconfig.json file.
 * Resolving here is best effort and might not work in all cases.
 * @beta
 */
export declare function getResolver(cwd?: string): NodeJS.RequireResolve;

/** Recursively navigates through a path, stripping nullability for key lookup. */
declare type NavigatePath<T, Path extends unknown[]> = Path extends [] ? NonNullish<T> : Path extends [infer K, ...infer Rest] ? K extends keyof NonNullish<T> ? NavigatePath<NonNullish<T>[K], Rest> : never : never;

/** Excludes `null` and `undefined` from a type. */
declare type NonNullish<T> = T extends null | undefined ? never : T;

/**
 * An error that occurred during query evaluation.
 * @public
 */
declare class QueryEvaluationError extends Error {
    filename: string;
    variable?: QueryVariable;
    constructor({ cause, filename, variable }: QueryEvaluationErrorOptions);
}

declare interface QueryEvaluationErrorOptions {
    cause: unknown;
    filename: string;
    variable?: QueryVariable;
}

/**
 * An error that occurred during query extraction.
 * @public
 */
export declare class QueryExtractionError extends Error {
    filename: string;
    variable?: QueryVariable;
    constructor({ cause, filename, variable }: QueryExtractionErrorOptions);
}

declare interface QueryExtractionErrorOptions {
    cause: unknown;
    filename: string;
    variable?: QueryVariable;
}

declare interface QueryVariable {
    id: t.Identifier;
    end?: number;
    start?: number;
}

/**
 * Read, parse and process a config file
 * @internal
 */
export declare function readConfig(path: string): Promise<TypeGenConfig>;

/**
 * Read a schema from a given path
 * @param path - The path to the schema
 * @returns The schema
 * @internal
 * @beta
 **/
export declare function readSchema(path: string): Promise<SchemaType>;

/**
 * Register Babel with the given options
 *
 * @param babelOptions - The options to use when registering Babel
 * @beta
 */
export declare function registerBabel(babelOptions?: TransformOptions): void;

/**
 * Runs a single typegen generation.
 *
 * This is the programmatic API for generating TypeScript types from GROQ queries.
 * It spawns a worker thread to perform the generation and displays progress via CLI spinners.
 *
 * @param options - Configuration options including typegen config and working directory
 * @returns Generation result containing the generated code and statistics
 */
export declare function runTypegenGenerate(options: RunTypegenOptions): Promise<GenerationResult>;

/**
 * Options for running a single typegen generation.
 * This is the programmatic API for one-off generation without file watching.
 */
export declare interface RunTypegenOptions {
    /** Working directory (usually project root) */
    workDir: string;
    /** Typegen configuration */
    config?: Partial<TypeGenConfig>;
    /** Optional spinner instance for progress display */
    spin?: ReturnType<typeof spinner>;
}

/**
 * Starts a file watcher that triggers typegen on changes.
 * Watches both query files (via patterns) and the schema JSON file.
 * Implements debouncing and concurrency control to prevent multiple generations.
 */
export declare function runTypegenWatcher(options: RunTypegenOptions): {
    getStats: () => WatcherStats;
    stop: () => Promise<void>;
    watcher: FSWatcher;
};

/**
 * safeParseQuery parses a GROQ query string, but first attempts to extract any parameters used in slices. This method is _only_
 * intended for use in type generation where we don't actually execute the parsed AST on a dataset, and should not be used elsewhere.
 * @internal
 */
export declare function safeParseQuery(query: string): ExprNode;

/** Builds a tuple from elements, stopping at the first `never`. */
declare type TakeUntilNever<T extends unknown[]> = T extends [infer H, ...infer Rest] ? [H] extends [never] ? [] : [H, ...TakeUntilNever<Rest>] : [];

/**
 * Statistics from the query type evaluation process.
 * @public
 */
declare interface TypeEvaluationStats {
    allTypes: number;
    emptyUnions: number;
    unknownTypes: number;
}

export declare type TypeGenConfig = z.infer<typeof configDefinition>;

/**
 * A class used to generate TypeScript types from a given schema
 * @beta
 */
export declare class TypeGenerator {
    private getSchemaTypeGenerator;
    private getSchemaTypeDeclarations;
    private getAllSanitySchemaTypesDeclaration;
    private getArrayOfDeclaration;
    private getInternalReferenceSymbolDeclaration;
    private static getEvaluatedModules;
    private static getQueryMapDeclaration;
    generateTypes(options: GenerateTypesOptions): Promise<{
        ast: t.Program;
        code: string;
    }>;
}

/**
 * @internal
 */
export declare class TypegenGenerateCommand extends SanityCommand<typeof TypegenGenerateCommand> {
    static description: string;
    static examples: {
        command: string;
        description: string;
    }[];
    static flags: {
        'config-path': OptionFlag<string | undefined, CustomOptions>;
        watch: BooleanFlag<boolean>;
    };
    run(): Promise<void>;
    private getConfig;
    private runSingle;
    private runWatcher;
}

export declare const TypegenWatchModeTrace: DefinedTelemetryTrace<TypegenWatchModeTraceAttributes, void>;

/**
 * Attributes for typegen watch mode trace - tracks the start and stop of watch mode
 * sessions with statistics about generation runs.
 */
declare type TypegenWatchModeTraceAttributes = {
    averageGenerationDuration: number;
    generationFailedCount: number;
    generationSuccessfulCount: number;
    step: 'stopped';
    watcherDuration: number;
} | {
    step: 'started';
};

export declare type TypegenWorkerChannel = WorkerChannel.Definition<{
    evaluatedModules: WorkerChannel.Stream<EvaluatedModule>;
    generatedQueryTypes: WorkerChannel.Event<{
        queryMapDeclaration: {
            ast: t.Program;
            code: string;
        };
    }>;
    generatedSchemaTypes: WorkerChannel.Event<{
        allSanitySchemaTypesDeclaration: {
            ast: t.ExportNamedDeclaration;
            code: string;
            id: t.Identifier;
        };
        internalReferenceSymbol: {
            ast: t.ExportNamedDeclaration;
            code: string;
            id: t.Identifier;
        };
        schemaTypeDeclarations: {
            ast: t.ExportNamedDeclaration;
            code: string;
            id: t.Identifier;
            name: string;
            tsType: t.TSType;
        }[];
    }>;
}>;

export declare const TypesGeneratedTrace: DefinedTelemetryTrace<TypesGeneratedTraceAttributes, void>;

declare interface TypesGeneratedTraceAttributes {
    configMethod: 'cli' | 'legacy';
    configOverloadClientMethods: boolean;
    emptyUnionTypeNodesGenerated: number;
    filesWithErrors: number;
    outputSize: number;
    queriesCount: number;
    queryFilesCount: number;
    schemaTypesCount: number;
    typeNodesGenerated: number;
    unknownTypeNodesGenerated: number;
    unknownTypeNodesRatio: number;
}

declare type WatcherStats = Omit<Extract<TypegenWatchModeTraceAttributes, {
    step: 'stopped';
}>, 'step'>;

export { }
