import { type IncomingHttpHeaders } from 'http';
import { EventManager, type IEvent } from '../events/event-manager';
import type { Request } from '../http/request';
import type { AllNodeMiddleware } from '../http/request-context';
import type { Class, PromiseOrValue, UnwrapPromise } from '../typescript';
import { type Validator } from '../validation';
import { ContextConsumer } from './context-consumer';
export interface DeclaroDependencies {
}
export interface DeclaroScope {
    requestMiddleware: ContextMiddleware<Context>[];
    nodeMiddleware: AllNodeMiddleware[];
}
export interface DeclaroRequestScope extends DeclaroScope {
    request: Request;
    headers: IncomingHttpHeaders;
    header: <K extends keyof IncomingHttpHeaders>(header: K) => IncomingHttpHeaders[K] | undefined;
}
export type ExtractScope<T extends Context<any>> = T extends Context<infer S> ? S : never;
/**
 * Creates a context type that narrows the scope to a subset.
 * This allows using a context with more dependencies where fewer are expected.
 */
export type NarrowContext<TContext extends Context<any>, TNarrowScope extends object> = TContext extends Context<infer TFullScope> ? TNarrowScope extends Partial<TFullScope> ? Context<TNarrowScope> : never : never;
export type ContextMiddleware<C extends Context = Context> = (context: Context<ExtractScope<C>>) => any | Promise<any>;
export type ContextState<TContext extends Context> = Record<PropertyKey, ContextAttribute<TContext, StateValue<any>>>;
export type ContextResolver<T> = (context: Context) => StateValue<T>;
export type StateValue<T> = T;
export declare enum DependencyType {
    VALUE = "VALUE",
    FACTORY = "FACTORY",
    CLASS = "CLASS"
}
export type FactoryFn<T, A extends any[]> = (...args: A) => T;
export type ValueLoader<C extends Context, T> = (context: C, resolutionOptions?: ResolveOptions) => T;
export type FilterKeysByType<TScope, TValue> = {
    [Key in keyof TScope]: TScope[Key] extends TValue ? Key : never;
}[keyof TScope];
export type FilterKeysByAsyncType<TScope, TValue> = {
    [Key in keyof TScope]: TScope[Key] extends PromiseOrValue<TValue> ? Key : never;
}[keyof TScope];
export type FilterArgsByType<TScope, TArgs extends any[]> = {
    [Key in keyof TArgs]: FilterKeysByType<TScope, TArgs[Key]>;
};
export type FilterAsyncArgsByType<TScope, TArgs extends any[]> = {
    [Key in keyof TArgs]: FilterKeysByAsyncType<TScope, TArgs[Key]>;
};
export type ContextAttribute<TContext extends Context<any>, TValue> = {
    key: PropertyKey;
    value?: ValueLoader<TContext, TValue>;
    type: DependencyType;
    resolveOptions?: ResolveOptions;
    cachedValue?: TValue;
    inject: PropertyKey[];
};
export type ScopeKey<S extends object> = keyof S;
export type ContextListener<C extends Context, E extends IEvent> = (context: C, event: E) => any;
/**
 * Interface for circular dependency proxies that defer to a real target once resolved.
 * These proxies are created during circular dependency resolution to allow references
 * to objects that haven't been fully constructed yet.
 */
export interface ResolveProxy<T = any> {
    /**
     * Identifies this object as a circular proxy.
     * @internal
     */
    readonly __isProxy: true;
    /**
     * Sets the real target object that this proxy should delegate to.
     * Called internally once the circular dependency is resolved.
     * @internal
     */
    readonly __resolve: (target: T) => void;
    /** Indicates whether the proxy has been resolved to a real target. */
    readonly __isResolved: boolean;
    /**
     * Returns the real target object if resolved, otherwise returns the proxy itself.
     * This allows using the proxy transparently before and after resolution.
     */
    readonly valueOf: () => T;
}
export declare function isProxy(value: any): value is ResolveProxy;
export interface ResolveOptions {
    /**
     * If true, an error will be thrown if the dependency is not found. If false, undefined will be returned if the dependency is not found.
     * @default false
     */
    strict?: boolean;
    /**
     * If true, the dependency will be resolved immediately when the context is initialized. This is useful for dependencies that need to perform setup work.
     * @default false
     */
    eager?: boolean;
    /**
     * If true, the dependency will be a singleton, and the same instance will be returned for every request. If false, a new instance will be created each time the dependency is resolved.
     * @default false
     */
    singleton?: boolean;
    /**
     * An optional resolution context that can be used to track resolution state across multiple `resolve` calls. This is primarily used internally to track circular dependencies, but can be useful for advanced use cases.
     */
    resolutionContext?: Map<PropertyKey, any>;
}
export declare function getNestedResolveOptions(options?: ResolveOptions | InternalResolveOptions): InternalResolveOptions;
export interface InternalResolveOptions extends ResolveOptions {
    resolutionStack: Set<PropertyKey>;
}
export declare function defaultResolveOptions(): ResolveOptions;
export type ContextOptions = {
    defaultResolveOptions?: ResolveOptions;
};
export declare class Context<Scope extends object = any> {
    private readonly state;
    private readonly emitter;
    readonly scope: Scope;
    protected readonly defaultResolveOptions: ResolveOptions;
    constructor(options?: ContextOptions);
    get events(): EventManager<IEvent>;
    initializeEagerDependencies(): Promise<void>;
    /**
     * Set a value in context, to be injected later.
     *
     * @param key
     * @param payload
     * @deprecated Use `provideValue` instead, or you can register the same dependency as a factory with `provideFactory` or class with `provideClass`.
     */
    provide<K extends ScopeKey<Scope>>(key: K, payload: Scope[K]): void;
    /**
     * Manually register a dependency. This should normally be used by utils or integrations that need to register dependencies in creative ways. For normal use cases, using `provideValue`, `provideFactory`, or `provideClass` is sufficient.
     *
     * @param key The key to register the dependency under
     * @param dep The dependency record
     */
    register<K extends ScopeKey<Scope>>(key: K, dep: ContextAttribute<this, Scope[K]>): void;
    /**
     * Add a dependency to the context.
     */
    protected addDep<K extends ScopeKey<Scope>>(key: K, dep: ContextAttribute<this, Scope[K]>): ContextAttribute<this, Scope[K]>;
    /**
     * Register a value in context scope.
     *
     * @param key The key to register the dependency under
     * @param value The value to register
     */
    registerValue<K extends ScopeKey<Scope>>(key: K, value: Scope[K], defaultResolveOptions?: ResolveOptions): this;
    /**
     * Register a dependency as a factory in context scope.
     *
     * @param key The key to register the dependency under
     * @param factory A factory function that will be called to generate the value when it is requested.
     * @param inject An array of keys to use when injecting factory args.
     * @returns A chainable instance of context
     */
    registerFactory<K extends ScopeKey<Scope>, A extends any[]>(key: K, factory: FactoryFn<Scope[K], A>, inject?: FilterArgsByType<Scope, A>, defaultResolveOptions?: ResolveOptions): this;
    registerAsyncFactory<K extends FilterKeysByType<Scope, Promise<any>>, A extends any[]>(key: K, factory: FactoryFn<Scope[K], A>, inject?: FilterAsyncArgsByType<Scope, A>, defaultResolveOptions?: ResolveOptions): this;
    registerClass<K extends FilterKeysByType<Scope, InstanceType<T>>, T extends Class<Scope[K] extends {} ? Scope[K] : never>>(key: K, Class: T, inject?: FilterArgsByType<Scope, ConstructorParameters<T>>, defaultResolveOptions?: ResolveOptions): this;
    registerAsyncClass<K extends FilterKeysByType<Scope, InstanceType<any>>, T extends Class<UnwrapPromise<Scope[K]> extends {} ? UnwrapPromise<Scope[K]> : never>>(key: K, Class: T, inject?: FilterAsyncArgsByType<Scope, ConstructorParameters<T>>, defaultResolveOptions?: ResolveOptions): this;
    getAllDependencies<K extends ScopeKey<Scope>>(key: K): ContextAttribute<this, any>[];
    getAllDependents<K extends ScopeKey<Scope>>(key: K, visited?: Set<any>): ContextAttribute<this, any>[];
    introspect<K extends ScopeKey<Scope>>(key: K): ContextState<this>[K];
    protected _cacheIsValid<K extends ScopeKey<Scope>>(key: K, visited?: Set<PropertyKey>): boolean;
    protected createProxy<T>(): ResolveProxy<T>;
    protected _resolveValue<K extends ScopeKey<Scope>>(key: K, resolveOptions?: InternalResolveOptions): Scope[K];
    /**
     * Extract a value from context.
     *
     * @param key
     * @returns
     * @deprecated Use `resolve` instead
     */
    inject<T = any>(key: ScopeKey<Scope>): T | undefined;
    resolve<K extends ScopeKey<Scope>>(key: K, resolveOptions?: ResolveOptions): Scope[K];
    /**
     * Ensure that only one copy of this instance exists in this context. Provides the instance if it doesn't exist yet, otherwise inject the cached instance.
     *
     * @param key
     * @param instance
     */
    singleton<T = any>(key: ScopeKey<Scope>, instance: T): T;
    /**
     * Instantiate a ContextConsumer class
     *
     * @param Consumer
     * @returns
     */
    hydrate<T extends ContextConsumer<this, any[]>, A extends any[]>(Consumer: new (context: this, ...args: A) => T, ...args: A): T;
    /**
     * Create a new context from other instance(s) of Context
     *
     * @param contexts
     * @returns
     */
    extend(...contexts: Context[]): this;
    /**
     * Modify context with middleware
     *
     * @param middleware
     * @returns
     */
    use<TNarrowScope extends Partial<Scope>>(...middleware: ContextMiddleware<Context<TNarrowScope>>[]): Promise<void>;
    use(...middleware: ContextMiddleware[]): Promise<void>;
    /**
     * Validate context ensuring all validators are valid.
     *
     * @param validators
     * @returns
     */
    validate(...validators: Validator<Context>[]): import("..").Validation<this>;
    /**
     * Validate context ensuring at least one validator is valid
     * @param validators
     * @returns
     */
    validateAny(...validators: Validator<Context>[]): import("..").Validation<any>;
    /**
     * Add a callback to listen for an event in this context.
     *
     * @param event
     * @param listener
     * @returns
     */
    on<E extends IEvent = IEvent>(type: IEvent['type'], listener: ContextListener<this, E>): () => void;
    /**
     * Emit an event in this context
     *
     * @param event
     * @param args
     * @returns
     */
    emit(event: string | IEvent): Promise<void>;
    /**
     * Narrow the context to a subset of its scope type.
     * This is a type-only operation - it returns the same instance with a narrower type.
     *
     * @returns A context with a narrower scope type that shares the same underlying state
     */
    narrow<TNarrowScope extends Partial<Scope>>(): NarrowContext<this, TNarrowScope>;
}
//# sourceMappingURL=context.d.ts.map