import { Merge, NoInfer, UnionToIntersection } from './utils';
export interface UnknownDependencyTree {
    readonly name: PropertyKey | never;
    readonly type: unknown;
    readonly children: readonly UnknownDependencyTree[];
    readonly optional: boolean;
}
type PickRequired<DependencyTree extends UnknownDependencyTree> = {
    readonly [Name in DependencyTree['name'] as DependencyTree['optional'] extends true ? never : Name]: DependencyTree['type'];
};
type PickOptional<DependencyTree extends UnknownDependencyTree> = {
    readonly [Name in DependencyTree['name'] as DependencyTree['optional'] extends true ? Name : never]?: DependencyTree['type'];
};
export type Flatten<Tree> = Tree extends UnknownDependencyTree ? PickRequired<Tree> & PickOptional<Tree> & UnionToIntersection<{
    readonly [Index in keyof Tree['children']]: Flatten<Tree['children'][Index]>;
}[number]> : never;
export interface InjectableWithoutName<Tree extends UnknownDependencyTree, Value> {
    (tree: Flatten<NoInfer<Tree>>): Value;
}
export interface InjectableWithName<Tree extends UnknownDependencyTree, Value> {
    (tree: Flatten<NoInfer<Tree>>): Value;
    readonly key: Tree['name'];
}
export type Injectable<Tree extends UnknownDependencyTree, Value> = InjectableWithoutName<Tree, Value> | InjectableWithName<Tree, Value>;
export type InjectableValue<Target> = Target extends Injectable<UnknownDependencyTree, infer Value> ? Value : never;
export type InjectableDependencyTree<Target> = Target extends Injectable<infer Tree, unknown> ? Tree : never;
export type InjectableDependencies<Target> = Merge<Flatten<InjectableDependencyTree<Target>>>;
type MapInjectablesToValues<Targets> = {
    readonly [Index in keyof Targets]: InjectableValue<Targets[Index]>;
};
export interface DependencyWithoutName<Result, Children> {
    readonly name: never;
    readonly type: Result;
    readonly optional: false;
    readonly children: Children;
}
export interface DependencyWithName<Name, Result, Children> {
    readonly name: Name;
    readonly type: Result;
    readonly optional: true;
    readonly children: Children;
}
export declare function injectable<Name extends PropertyKey, Result>(name: Name, project: () => Result): InjectableWithName<DependencyWithName<Name, Result, []>, Result>;
export declare function injectable<Result>(project: () => Result): InjectableWithoutName<DependencyWithoutName<Result, []>, Result>;
export declare function injectable<Inputs extends Record<PropertyKey, Injectable<UnknownDependencyTree, unknown>>, Result>(inputs: Inputs, project: (values: {
    readonly [Key in keyof Inputs]: InjectableValue<Inputs[Key]>;
}) => Result): InjectableWithoutName<DependencyWithoutName<Result, {
    [Key in keyof Inputs]: InjectableDependencyTree<Inputs[Key]>;
}[keyof Inputs][]>, Result>;
export declare function injectable<Name extends PropertyKey, Inputs extends Record<PropertyKey, Injectable<UnknownDependencyTree, unknown>>, Result>(name: Name, inputs: Inputs, project: (values: {
    readonly [Key in keyof Inputs]: InjectableValue<Inputs[Key]>;
}) => Result): InjectableWithName<DependencyWithName<Name, Result, {
    [Key in keyof Inputs]: InjectableDependencyTree<Inputs[Key]>;
}[keyof Inputs][]>, Result>;
export declare function injectable<Name extends PropertyKey, Inputs extends readonly Injectable<UnknownDependencyTree, unknown>[], Result>(name: Name, ...args: [...Inputs, (...values: MapInjectablesToValues<Inputs>) => Result]): InjectableWithName<DependencyWithName<Name, Result, {
    readonly [Index in keyof Inputs]: InjectableDependencyTree<Inputs[Index]>;
}>, Result>;
export declare function injectable<Inputs extends readonly Injectable<UnknownDependencyTree, unknown>[], Result>(...args: [...Inputs, (...values: MapInjectablesToValues<Inputs>) => Result]): InjectableWithoutName<DependencyWithoutName<Result, {
    readonly [Index in keyof Inputs]: InjectableDependencyTree<Inputs[Index]>;
}>, Result>;
export {};
