import { Faker } from '@faker-js/faker';
import { LocaleDefinition } from '@faker-js/faker';
import { Randomizer } from '@faker-js/faker';
import { z } from 'zod/v4';
import { ZodObject } from 'zod/v4';
import { ZodType } from 'zod/v4';

declare type AfterBuildHook<T> = (obj: T) => Promise<T> | T;

declare type BeforeBuildHook<T> = (params: Partial<T>) => Partial<T> | Promise<Partial<T>>;

declare interface CreateManyOptions<T> {
    adapter?: PersistenceAdapter<T>;
}

declare interface CreateOptions<T> {
    adapter?: PersistenceAdapter<T>;
}

declare interface DateSequenceOptions {
    start: Date | number | string;
    step?: number;
    unit?: 'day' | 'hour' | 'minute';
}

/**
 * A factory class for generating type-safe mock data by extending Faker.js functionality.
 * Provides methods for creating single instances, batches, and complex object compositions
 * with support for circular references through depth control and persistence.
 *
 * @template T - The type of objects this factory generates
 * @template O - The type of factory options
 */
declare class Factory<T, O extends FactoryOptions = FactoryOptions, F extends FactoryFunction<T> | PartialFactoryFunction<T> = FactoryFunction<T>> extends Faker {
    #private;
    readonly options?: {
        maxDepth: number;
    } & Omit<O, 'locale' | 'maxDepth' | 'randomizer'>;
    protected afterBuildHooks: AfterBuildHook<T>[];
    protected beforeBuildHooks: BeforeBuildHook<T>[];
    protected readonly factory: F;
    private defaultAdapter?;
    readonly sequence: SequenceFactory;
    constructor(factory: F, { locale, randomizer, ...rest }?: Partial<O>);
    /**
     * Adds a hook that will be executed after building the instance.
     * Hooks are executed in the order they were added and can be either synchronous or asynchronous.
     * This method returns the factory instance for method chaining.
     *
     * @param hook Function that receives the built instance and returns the modified instance
     * @returns The current Factory instance for method chaining
     */
    afterBuild(hook: AfterBuildHook<T>): this;
    /**
     * Generates an array of instances using the factory's schema.
     * Supports both uniform overrides (same for all instances) and individual overrides per instance.
     *
     * @param size Number of instances to generate (must be non-negative integer)
     * @param kwargs Either a single partial object (applied to all) or an array of partials (one per instance)
     * @returns Array of generated instances
     * @throws {Error} If size is negative or not an integer
     */
    batch: (size: number, kwargs?: Partial<T> | Partial<T>[]) => T[];
    /**
     * Creates multiple instances asynchronously, allowing use of async factory functions.
     * This method supports both synchronous and asynchronous factory functions and hooks.
     *
     * @param size Number of instances to generate (must be non-negative integer)
     * @param kwargs Either a single partial object (applied to all) or an array of partials (one per instance)
     * @returns Promise that resolves to an array of generated instances
     * @throws {Error} If size is negative or not an integer
     *
     * @example
     * ```typescript
     * const UserFactory = new Factory<User>(async (factory) => ({
     *   id: factory.string.uuid(),
     *   email: factory.internet.email(),
     *   apiKey: await generateApiKey() // async operation
     * }));
     *
     * const users = await UserFactory.batchAsync(5);
     * ```
     */
    batchAsync(size: number, kwargs?: Partial<T> | Partial<T>[]): Promise<F extends FactoryFunction<T> ? T[] : Partial<T>[]>;
    /**
     * Adds a hook that will be executed before building the instance.
     * Hooks receive the partial parameters (kwargs) and can modify them before the instance is built.
     * Multiple hooks are executed in the order they were added.
     *
     * @param hook Function that receives partial parameters and returns modified parameters
     * @returns The current Factory instance for method chaining
     */
    beforeBuild(hook: BeforeBuildHook<T>): this;
    /**
     * Generates a single instance of type T using the factory's schema.
     * Properties can be overridden by passing a partial object.
     * Synchronous hooks are automatically applied if registered.
     * If async hooks are registered, a ConfigurationError is thrown.
     *
     * @param kwargs Properties to override in the generated instance
     * @param options Factory options including fixture generation
     * @returns A new instance with factory-generated values merged with any overrides
     * @throws {ConfigurationError} If async hooks are registered
     * @throws {FixtureError} If fixture operations fail
     * @throws {FixtureValidationError} If fixture validation fails
     */
    build: (kwargs?: Partial<T>, options?: Partial<O>) => F extends FactoryFunction<T> ? T : Partial<T>;
    /**
     * Builds an instance asynchronously with all registered hooks applied in sequence.
     * This method supports both synchronous and asynchronous hooks.
     * Hooks are executed in the order they were registered.
     *
     * @param kwargs Optional properties to override in the generated instance
     * @param options Factory options including fixture generation
     * @returns A promise that resolves to the built and processed instance
     * @throws {Error} If any hook throws an error during execution
     * @throws {FixtureError} If fixture operations fail
     * @throws {FixtureValidationError} If fixture validation fails
     */
    buildAsync(kwargs?: Partial<T>, options?: Partial<O>): Promise<F extends FactoryFunction<T> ? T : Partial<T>>;
    /**
     * Creates a new factory by merging this factory's schema with additional properties.
     * Composed properties can be static values or other factory instances.
     *
     * @template U The composed type (must extend the base type T)
     * @param composition Object mapping property names to values or factories
     * @returns A new factory that generates objects with combined properties
     */
    compose<U extends T>(composition: FactoryComposition<U>): Factory<U>;
    /**
     * Creates and persists a single instance using the factory's schema.
     * Uses the configured persistence adapter if available.
     *
     * @param kwargs Optional properties to override in the generated instance
     * @param options Options including an optional persistence adapter
     * @returns Promise that resolves with the persisted instance
     * @throws {ConfigurationError} If no persistence adapter is configured
     *
     * @example
     * ```typescript
     * // With default adapter
     * const userFactory = new Factory<User>((faker) => ({
     *   email: faker.internet.email(),
     *   name: faker.person.fullName()
     * })).withAdapter(mongooseAdapter);
     *
     * const user = await userFactory.create({ name: 'John' });
     *
     * // With adapter in options
     * const user2 = await userFactory.create(
     *   { name: 'Jane' },
     *   { adapter: prismaAdapter }
     * );
     * ```
     */
    create(kwargs?: Partial<T>, options?: CreateOptions<T>): Promise<T>;
    /**
     * Creates and persists multiple instances in a batch operation.
     * Uses the configured persistence adapter if available.
     *
     * @param size Number of instances to create and persist
     * @param kwargs Optional overrides for the instances
     * @param options Options including an optional persistence adapter
     * @returns Promise that resolves with the persisted instances
     * @throws {ConfigurationError} If no persistence adapter is configured
     *
     * @example
     * ```typescript
     * // Create 5 users with default adapter
     * const users = await userFactory.createMany(5);
     *
     * // With individual overrides
     * const users2 = await userFactory.createMany(3, [
     *   { role: 'admin' },
     *   { role: 'user' },
     *   { role: 'guest' }
     * ]);
     *
     * // With adapter in options
     * const users3 = await userFactory.createMany(
     *   10,
     *   undefined,
     *   { adapter: typeormAdapter }
     * );
     * ```
     */
    createMany(size: number, kwargs?: Partial<T> | Partial<T>[], options?: CreateManyOptions<T>): Promise<T[]>;
    /**
     * Creates a new factory that inherits from this factory's schema with modifications.
     * Unlike compose(), extend() provides access to the factory instance for dynamic property generation.
     *
     * @template U The extended type (must extend the base type T)
     * @param factoryFn Function that returns properties to merge with the base schema
     * @returns A new factory with inherited and extended properties
     */
    extend<U extends T>(factoryFn: FactoryFunction<U>): Factory<U>;
    /**
     * Creates a generator that yields values from an iterable in sequential, cyclic order.
     * Values must be explicitly requested via the generator's next() method. When all values
     * have been yielded, the generator starts over from the beginning.
     *
     * @template T The type of elements in the iterable
     * @param iterable An iterable containing values to cycle through (must not be empty)
     * @returns A generator that yields values in order, restarting after the last element
     * @throws {Error} If the iterable is empty
     */
    iterate<U>(iterable: Iterable<U>): Generator<U, U, U>;
    /**
     * Creates a new factory where all properties are optional (Partial<T>).
     * This is useful for creating test data where only specific fields need to be set.
     *
     * @returns A new factory that produces Partial<T> objects
     */
    partial(): Factory<Partial<T>>;
    /**
     * Creates a generator that yields random values from an iterable without consecutive duplicates.
     * Each value is randomly selected with replacement, but the generator ensures the same value
     * is never returned twice in a row (unless the iterable contains only one element).
     *
     * @template T The type of elements in the iterable
     * @param iterable An iterable containing values to sample from (must not be empty)
     * @returns A generator that yields random values without consecutive repetition
     * @throws {Error} If the iterable is empty
     */
    sample<U>(iterable: Iterable<U>): Generator<U, U, U>;
    /**
     * Creates a reference to a function call for lazy evaluation within factory definitions.
     * The function and its arguments are stored but not executed until the factory builds an object.
     * Essential for creating relationships between factories without causing infinite recursion.
     *
     * @template C The function type
     * @param handler The function to call during object generation
     * @param args Arguments to pass to the function when called
     * @returns A reference that will execute the function during build
     */
    use<R, A extends unknown[]>(handler: (...args: A) => R, ...args: A): R;
    /**
     * Sets the default persistence adapter for this factory instance.
     *
     * @param adapter The persistence adapter to use as default
     * @returns The current Factory instance for method chaining
     *
     * @example
     * ```typescript
     * import { MongooseAdapter } from './adapters/mongoose-adapter';
     *
     * const userFactory = new Factory<User>((faker) => ({
     *   id: faker.string.uuid(),
     *   email: faker.internet.email(),
     *   name: faker.person.fullName()
     * }));
     *
     * // Set default adapter
     * const factoryWithDb = userFactory.withAdapter(
     *   new MongooseAdapter(UserModel)
     * );
     *
     * // Now all create/createMany calls will use this adapter
     * const user = await factoryWithDb.create();
     * const users = await factoryWithDb.createMany(5);
     * ```
     */
    withAdapter(adapter: PersistenceAdapter<T>): this;
    protected buildWithFixture(filePath: string, kwargs: Partial<T> | undefined, _options: FactoryOptions & O): F extends FactoryFunction<T> ? T : Partial<T>;
    protected buildWithFixtureAsync(filePath: string, kwargs: Partial<T> | undefined, _options: FactoryOptions & O): Promise<F extends FactoryFunction<T> ? T : Partial<T>>;
    protected calculateSignature(config: Required<FixtureConfiguration>): string;
    /* Excluded from this release type: createDepthLimitedProxy */
    protected getDefaultFixturePath(): string;
    protected getFixtureConfig(): Required<FixtureConfiguration>;
    /* Excluded from this release type: isDepthExceeded */
    protected parseFixturePath(filePath: string, config: Required<FixtureConfiguration>): {
        fixturesDir: string;
        fullPath: string;
    };
    protected readFixture(fullPath: string): FixtureMetadata | null;
    protected validateFixture(metadata: FixtureMetadata, config: Required<FixtureConfiguration>): void;
    protected writeFixture(parsedPath: {
        fixturesDir: string;
        fullPath: string;
    }, data: unknown, config: Required<FixtureConfiguration>): void;
}

declare type FactoryComposition<T> = {
    [K in keyof T]?: Factory<T[K]> | T[K];
};

declare type FactoryFunction<T> = (factory: Factory<T>, iteration: number, kwargs?: Partial<T>) => FactorySchema<T> | Promise<FactorySchema<T>>;

declare interface FactoryOptions {
    /**
     * Fixture configuration for caching generated data
     */
    fixtures?: FixtureConfiguration;
    /**
     * Enable fixture generation/loading for this build.
     * - true: Use default fixture path based on call stack
     * - string: Use as fixture file path
     */
    generateFixture?: boolean | string;
    locale?: LocaleDefinition | LocaleDefinition[];
    maxDepth?: number;
    randomizer?: Randomizer;
}

declare type FactorySchema<T> = {
    [K in keyof T]: Generator<T[K], T[K], T[K]> | Ref<T[K], (...args: unknown[]) => T[K]> | T[K];
};

declare interface FixtureConfiguration {
    /**
     * Base directory for storing fixtures. Defaults to process.cwd()
     */
    basePath?: string;
    /**
     * Custom directory name for fixtures. Defaults to '__fixtures__'
     * Set to empty string to store fixtures in the same directory as the file path
     */
    directory?: string;
    /**
     * Whether to include the factory function source in signature calculation. Defaults to true
     */
    includeSource?: boolean;
    /**
     * Whether to use subdirectories for fixtures. Defaults to true
     * When true: fixtures are stored in a subdirectory (e.g., /path/__fixtures__/file.json)
     * When false: fixtures are stored directly in the path (e.g., /path/file.json)
     */
    useSubdirectory?: boolean;
    /**
     * Whether to validate factory signature changes. Defaults to true
     */
    validateSignature?: boolean;
}

declare interface FixtureMetadata {
    /**
     * ISO 8601 timestamp when the fixture was created
     */
    createdAt: string;
    /**
     * The actual fixture data
     */
    data: unknown;
    /**
     * SHA-256 hash of the factory configuration
     */
    signature: string;
    /**
     * Fixture version for future compatibility
     */
    version: number;
}

declare interface IncrementSequenceOptions {
    start?: number;
    step?: number;
}

declare type PartialFactoryFunction<T> = (factory: Factory<T>, iteration: number) => Partial<FactorySchema<T>> | Promise<Partial<FactorySchema<T>>>;

/**
 * Interface for a persistence adapter.
 * Adapters are responsible for interacting with the underlying data store
 * (e.g., database, API) to save generated objects.
 */
declare interface PersistenceAdapter<T, R = T> {
    /**
     * Persists a single generated object to the data store.
     *
     * @param data The object to persist.
     * @returns A promise that resolves with the persisted object,
     * including any properties assigned by the database (e.g., auto-generated IDs).
     */
    create(data: T): Promise<R>;
    /**
     * Persists multiple generated objects to the data store in a batch.
     *
     * @param data An array of objects to persist.
     * @returns A promise that resolves with an array of the persisted objects,
     * including any properties assigned by the database.
     */
    createMany(data: T[]): Promise<R[]>;
}

/**
 * Encapsulates a function and its arguments for deferred execution within factories.
 * This enables lazy evaluation of nested factory calls, preventing infinite recursion
 * and allowing complex object relationships to be defined declaratively.
 *
 * @template T - The return type of the encapsulated function
 * @template C - The function type that returns T
 */
declare class Ref<T, C extends (...args: never[]) => T> {
    private readonly args;
    private readonly handler;
    constructor({ args, handler }: {
        args: Parameters<C>;
        handler: C;
    });
    callHandler(): T;
}

declare interface SequenceFactory {
    date(options: DateSequenceOptions): Generator<Date, Date, Date>;
    increment(options?: IncrementSequenceOptions): Generator<number, number, number>;
    template(pattern: string, options?: TemplateSequenceOptions): Generator<string, string, string>;
}

declare interface TemplateSequenceOptions {
    start?: number;
}

/**
 * A factory class for generating type-safe mock data from Zod schemas.
 *
 * ZodFactory extends the base Factory class to provide automatic generation of mock data
 * that conforms to Zod schema definitions. It supports all major Zod types including
 * primitives, objects, arrays, unions, enums, and more.
 *
 * Note: This class requires the `zod` package to be installed and uses Zod v4 API.
 *
 * @example
 * Basic usage with a simple schema:
 * ```typescript
 * import { z } from 'zod/v4';
 * import { ZodFactory } from 'interface-forge/zod';
 *
 * const UserSchema = z.object({
 *   id: z.string().uuid(),
 *   name: z.string().min(1).max(100),
 *   email: z.string().email(),
 *   age: z.number().int().min(18).max(120)
 * });
 *
 * const factory = new ZodFactory(UserSchema);
 * const user = factory.build();
 * ```
 *
 * @example
 * Using partial factory functions for custom generation:
 * ```typescript
 * const factory = new ZodFactory(UserSchema, (faker) => ({
 *   name: faker.person.fullName(),
 * }));
 * ```
 *
 * @template T - Must be a ZodObject type that defines the schema shape
 * @template O - Factory options extending ZodFactoryOptions
 *
 * @see {@link https://github.com/goldziher/interface-forge/blob/main/examples/07-zod-basic.ts | Basic Example}
 * @see {@link https://github.com/goldziher/interface-forge/blob/main/examples/07-zod-integration.ts | Advanced Example}
 * @see {@link https://github.com/goldziher/interface-forge/blob/main/examples/07-zod-testing.ts | Testing Example}
 */
export declare class ZodFactory<T extends ZodObject<any, any>, O extends ZodFactoryOptions = ZodFactoryOptions> extends Factory<z.output<T>, O> {
    private readonly generator;
    private readonly schema;
    /**
     * Creates a new ZodFactory instance.
     *
     * @param schema - The Zod object schema to generate data from
     * @param optionsOrFactory - Either factory options or a partial factory function
     * @param options - Factory options (when second parameter is a factory function)
     *
     * @example
     * Simple schema-based generation:
     * ```typescript
     * const factory = new ZodFactory(UserSchema);
     * ```
     *
     * @example
     * With partial factory function:
     * ```typescript
     * const factory = new ZodFactory(UserSchema, (faker) => ({
     *   name: faker.person.fullName(),
     *   // Other fields auto-generated
     * }));
     * ```
     *
     * @example
     * With options:
     * ```typescript
     * const factory = new ZodFactory(UserSchema, {
     *   maxDepth: 3,
     *   generators: { userId: () => 'custom-id' }
     * });
     * ```
     */
    constructor(schema: T, optionsOrFactory?: O | PartialFactoryFunction<z.output<T>>, options?: O);
    /**
     * Generates a batch of instances that conform to the Zod schema.
     *
     * @param size - Number of instances to generate
     * @param kwargs - Optional overrides for each instance
     * @returns Array of generated instances
     */
    batch: (size: number, kwargs?: Partial<z.output<T>> | Partial<z.output<T>>[]) => z.output<T>[];
    /**
     * Builds a single instance that conforms to the Zod schema.
     *
     * The build process:
     * 1. Generates data from the Zod schema constraints
     * 2. Applies any factory function customizations
     * 3. Applies the provided overrides
     * 4. Validates the result against the schema
     *
     * @param kwargs - Optional property overrides
     * @param options - Optional build options including fixture generation
     * @returns A generated instance conforming to the schema
     *
     * @example
     * ```typescript
     * const user = factory.build({
     *   role: 'admin',
     *   isActive: true
     * });
     * ```
     *
     * @example
     * With fixture generation:
     * ```typescript
     * const user = factory.build(undefined, { generateFixture: 'user-fixture' });
     * ```
     */
    build: (kwargs?: Partial<z.output<T>>, options?: Partial<O>) => z.output<T>;
    /**
     * Register a custom handler for a specific Zod type.
     * Allows customization of how specific Zod types are generated.
     *
     * @param typeName - The Zod type name (e.g., 'ZodCustom', 'ZodBigInt')
     * @param handler - Function that generates values for this type
     * @returns The factory instance for method chaining
     *
     * @example
     * const factory = new ZodFactory(schema)
     *   .withTypeHandler('ZodCustom', (schema, generator) => ({
     *     customField: generator.factory.lorem.word()
     *   }))
     *   .withTypeHandler('ZodBigInt', () => BigInt(42));
     */
    withTypeHandler(typeName: string, handler: ZodTypeHandler): this;
    /**
     * Register multiple type handlers at once.
     *
     * @param handlers - Object mapping type names to handler functions
     * @returns The factory instance for method chaining
     *
     * @example
     * const factory = new ZodFactory(schema).withTypeHandlers({
     *   ZodCustom: (schema, generator) => ({ id: generator.factory.string.uuid() }),
     *   ZodBigInt: () => BigInt(Math.floor(Math.random() * 1000))
     * });
     */
    withTypeHandlers(handlers: Record<string, ZodTypeHandler>): this;
    /**
     * Override buildWithFixture to handle Zod-specific generation
     *
     * @param filePath The fixture file path
     * @param kwargs Optional property overrides
     * @param _options Build options
     * @returns The generated Zod object
     */
    protected buildWithFixture(filePath: string, kwargs?: Partial<z.output<T>>, _options?: Partial<O>): z.output<T>;
    /**
     * Override signature calculation to include Zod schema information
     *
     * @param config Fixture configuration
     * @returns SHA-256 hash of the factory signature
     */
    protected calculateSignature(config: Required<FixtureConfiguration>): string;
}

/**
 * Options for configuring ZodFactory behavior.
 *
 */
export declare interface ZodFactoryOptions extends FactoryOptions {
    /**
     * Custom generator functions for specific field types.
     *
     * Generators are matched by the field's description metadata.
     *
     * @example
     * ```typescript
     * const factory = new ZodFactory(schema, {
     *   generators: {
     *     // Schema field must have .describe('userId')
     *     userId: () => `USR_${Date.now()}`,
     *     customEmail: () => `test_${Date.now()}@example.com`
     *   }
     * });
     * ```
     */
    generators?: Record<string, () => unknown>;
}

/**
 * Schema generator class that handles recursive generation of values from any ZodType.
 * This class is responsible for all the complex schema traversal and generation logic.
 */
declare class ZodSchemaGenerator {
    readonly factory: ZodFactory<never>;
    private dateFormatGenerators;
    private readonly NOT_FOUND;
    private stringFormatGenerators;
    private readonly typeHandlers;
    constructor(factory: ZodFactory<never>);
    /**
     * Generate a value from any ZodType schema.
     *
     * @param schema
     * @param currentDepth
     *
     * @returns The generated value
     */
    generateFromSchema(schema: ZodType, currentDepth?: number): unknown;
    getDepthLimitFallback(schema: ZodType): unknown;
    /**
     * Register a custom handler for a specific Zod type.
     *
     * @param typeName
     * @param handler
     *
     * @returns The factory instance
     */
    registerTypeHandler(typeName: string, handler: ZodTypeHandler): this;
    /**
     * Register multiple type handlers at once.
     *
     * @param handlers
     *
     * @returns The factory instance
     */
    registerTypeHandlers(handlers: Record<string, ZodTypeHandler>): this;
    private calculateArrayLength;
    private calculateTargetLength;
    private extractSizeConstraints;
    private generateArray;
    private generateDate;
    private generateFromRegex;
    private generateFromZodType;
    private generateMap;
    private generateNumber;
    private generateObject;
    private generateRecord;
    private generateSet;
    private generateString;
    private generateStringWithConstraints;
    private generateTuple;
    private initializeBuiltinHandlers;
    private tryGenerateFromMapping;
    private tryGenerateFromMetadata;
    private tryGenerateFromTypeHandler;
}

declare type ZodTypeHandler = (schema: ZodType, generator: ZodSchemaGenerator, currentDepth: number) => unknown;

export { }
