import { ZodObject, ZodDefault, z } from 'zod';

type DefaultMaxDepth = 8;
type HasDefaults<S> = S extends {
    _zod: {
        def: {
            defaultValue: unknown;
        };
    };
} ? true : S extends ZodObject<infer Shape> ? {
    [K in keyof Shape]: HasDefaults<Shape[K]>;
} extends Record<keyof Shape, true> ? true : false : false;
type UnwrapSchema<T> = T extends ZodDefault<infer U> ? U : T;
type AllFieldsHaveDefaults<Schema> = Schema extends {
    _zod: {
        def: {
            defaultValue: unknown;
        };
    };
} ? true : HasDefaults<UnwrapSchema<Schema>>;
interface AppScopeConfig<FlagSchema extends ZodObject<any> | undefined = undefined, FactSchema extends ZodObject<any> | undefined = undefined> {
    flagSchema: FlagSchema;
    factSchema?: FactSchema;
}
/**
 * Recursive type to extract all possible paths from an object type.
 * Uses stack-based depth limiting for better performance.
 *
 * @template T - The object type to extract paths from
 * @template Stack - Internal stack counter (do not set manually)
 * @template MaxDepth - Maximum recursion depth (default: 8 for good balance)
 */
type ObjectPaths<T, Stack extends unknown[] = [], MaxDepth extends number = DefaultMaxDepth> = Stack['length'] extends MaxDepth ? never : T extends object ? {
    [K in keyof T]-?: K extends string | number ? `${K}` | `${K}.${ObjectPaths<T[K], [1, ...Stack], MaxDepth>}` : never;
}[keyof T] : never;
type ObjectPathValue<T, P extends string> = P extends keyof T ? T[P] : P extends `${infer K}.${infer Rest}` ? K extends keyof T ? ObjectPathValue<T[K], Rest> : never : never;
/**
 * Generate deep nested paths from flag schema.
 *
 * @template T - ZodObject to extract paths from
 * @template MaxDepth - Maximum recursion depth (default: 8, override for deeper nesting)
 * @example
 * // Default 8-level depth
 * type Paths = DotPaths<MySchema>
 *
 * // Custom depth for deeper nesting (impacts performance)
 * type DeepPaths = DotPaths<MySchema, 12>
 */
type DotPaths<T extends ZodObject<any>, MaxDepth extends number = DefaultMaxDepth> = {
    [NS in keyof T['shape']]: (string & NS) | {
        [P in ObjectPaths<z.output<UnwrapSchema<T['shape'][NS]>>, [
        ], MaxDepth>]: `${string & NS}.${P}`;
    }[ObjectPaths<z.output<UnwrapSchema<T['shape'][NS]>>, [], MaxDepth>];
}[keyof T['shape']];
type PathValue<T extends ZodObject<any>, P extends string> = P extends `${infer NS}.${infer Rest}` ? NS extends keyof T['shape'] ? ObjectPathValue<z.output<UnwrapSchema<T['shape'][NS]>>, Rest> : never : P extends keyof T['shape'] ? z.output<UnwrapSchema<T['shape'][P]>> : never;
type DotNotationFlagFunction<FS extends ZodObject<any> | undefined> = FS extends ZodObject<any> ? <P extends DotPaths<FS>>(path: P) => PathValue<FS, P> : never;
type FactFunction<SC extends ZodObject<any> | undefined> = SC extends ZodObject<any> ? <P extends DotPaths<SC> & string>(name: P, value: PathValue<SC, P>) => void : never;
type OverrideFlagsFunction<FS extends ZodObject<any> | undefined> = FS extends ZodObject<any> ? (partial: {
    [K in DotPaths<FS>]?: PathValue<FS, K>;
}) => void : (partial: Record<string, any>) => void;
type WithFlagsFunction<FS extends ZodObject<any> | undefined> = FS extends ZodObject<any> ? <T>(overrides: {
    [K in DotPaths<FS>]?: PathValue<FS, K>;
}, fn: () => T) => T : <T>(overrides: Record<string, any>, fn: () => T) => T;
type PickFlagsFunction<FS extends ZodObject<any> | undefined> = FS extends ZodObject<any> ? {
    <K extends ReadonlyArray<DotPaths<FS> & string>>(...paths: K): K;
    <K extends ReadonlyArray<DotPaths<FS> & string>>(paths: K): K;
} : never;
interface AppScope<FS extends ZodObject<any> | undefined, SC extends ZodObject<any> | undefined> {
    flag: DotNotationFlagFunction<FS>;
    fact: FactFunction<SC>;
    overrideFlags: OverrideFlagsFunction<FS>;
    withFlags: WithFlagsFunction<FS>;
    pickFlags: PickFlagsFunction<FS>;
    getAllDefaultFlags: () => Record<string, any>;
}
/**
 * Create a new application-level evaluation scope.
 *
 * @param config.flagSchema A zod object describing the schema for flags **(required)**
 * @param config.factSchema A zod object describing the schema for facts (optional)
 *
 * @example
 * import { z } from 'zod';
 *
 * const { flag, fact, withFlags, pickFlags, overrideFlags } = createAppScope({
 *   flagSchema: z.object({
 *     ui: z.object({
 *       darkMode: z.boolean().default(false),
 *       theme:    z.object({
 *         primary: z.string().default('#00f'),
 *       }),
 *     }),
 *     api: z.object({
       endpoint: z.string().default('/api')
     }),
 *   }),
 *   factSchema: z.object({
 *     userAction: z.string(),
 *     timing: z.number(),
 *   }),
 * });
 *
 * // Typed flag access
 * const dark = flag('ui.darkMode'); // inferred boolean
 * const theme = flag('ui.theme'); // entire object
 * const primary = flag('ui.theme.primary'); // '#00f'
 * const endpoint = flag('api.endpoint'); // uses schema default
 *
 * // Typed fact recording
 * fact('userAction', 'clicked_button');
 * fact('timing', 1250);
 *
 * // Temporarily override flags for a block of code
 * withFlags({ 'ui.darkMode': true }, () => {
 *   // code here, `ui.darkMode` will be true in this block and reset after
 * });
 *
 * // Override flags globally for the current evaluation run
 * overrideFlags({ 'api.endpoint': '/custom' });
 */
declare function createAppScope<FlagSchema extends ZodObject<any>, FactSchema extends ZodObject<any> | undefined = undefined>(config: AllFieldsHaveDefaults<FlagSchema> extends true ? AppScopeConfig<FlagSchema, FactSchema> : {
    flagSchema: FlagSchema;
    factSchema?: FactSchema;
    __error__: 'createAppScope: flagSchema must have .default() for all leaf fields';
}): AppScope<FlagSchema, FactSchema>;

export { createAppScope as c };
