import { AnyHollywood, InferContainer, RegisterTokens, ContainerOptions, HollywoodOf, Hollywood } from 'hollywood-di';
import { Sade } from 'sade';

type Spread<T> = {
    [K in keyof T]: T[K];
} & {};
type Merge<T, To> = T & Omit<To, keyof T>;
type IsEmptyObject<T> = keyof T extends never ? true : false;
type IsCapitalized<T extends string> = Uncapitalize<T> extends T ? false : true;
type CamelToKebabCaseKey<T extends PropertyKey, A extends string = ""> = T extends string ? T extends `${infer P}${infer S}` ? Uncapitalize<S extends "" ? `${A}${P}` : IsCapitalized<S> extends true ? `${A}${P}-${CamelToKebabCaseKey<Uncapitalize<S>>}` : CamelToKebabCaseKey<S, `${A}${P}`>> : T : never;
type StripNegated<T extends PropertyKey> = T extends `no-${infer U}` ? U : T;
type MapUnion<T extends string, U extends Record<T, any>> = U extends Record<T, any> ? U : never;
type KnownKey<T> = string extends T ? never : number extends T ? never : symbol extends T ? never : T;
type KnownMappedKeys<T> = {
    [K in keyof T as KnownKey<K>]: T[K];
} & {};

type ArgType = "required" | "optional";
type ArgTypeToValue<T extends ArgType> = MapUnion<T, {
    required: string;
    optional: string | undefined;
}>[T];
type ParsedArgs<Args extends Record<string, Arg>> = {
    [K in keyof Args as KnownKey<K>]: Args[K] extends Arg<infer T> ? ArgTypeToValue<T> : never;
};
declare class Arg<T extends ArgType = ArgType> {
    private _type;
    constructor(_type: T);
    optional(): Arg<"optional">;
    static toObject(arg: Arg): {
        type: ArgType;
    };
    static toString(arg: Arg, name: string): string;
}
declare function arg(): Arg<"required">;

type ParamType = "string" | "number" | "array";
type ParamTypeToValue<T extends ParamType> = MapUnion<T, {
    string: string;
    number: number;
    array: string[];
}>[T];
type OptionalParam<T extends ParamType, D extends ParamTypeToValue<T> | undefined> = {
    _type: "optional";
    type: T;
    defaultValue: D;
};
type RequiredParam<T extends ParamType> = {
    _type: "required";
    type: T;
};
type FlagKey<F extends Record<string, Flag>, K extends keyof F> = F[K] extends Flag<infer TFlag> ? (TFlag["preserveCase"] extends true ? StripNegated<K> : StripNegated<CamelToKebabCaseKey<K>>) | (TFlag["char"] extends string ? TFlag["char"] : never) : never;
type TFlag = {
    char?: string;
    param?: OptionalParam<any, any> | RequiredParam<any>;
    preserveCase?: boolean;
};
type Replace<T, K extends keyof T, N extends T[K]> = Spread<Omit<T, K> & {
    [k in K]: N;
}>;
type Remove<T, K extends keyof T> = Spread<Omit<T, K>>;
type ParsedFlags<Flags extends Record<string, Flag>> = {
    [K in keyof Flags as KnownKey<FlagKey<Flags, K>>]: Flags[K] extends Flag<infer T> ? T["param"] extends RequiredParam<infer Type> ? ParamTypeToValue<Type> : T["param"] extends OptionalParam<infer Type, infer D> ? undefined extends D ? ParamTypeToValue<Type> | undefined : ParamTypeToValue<Type> : T["param"] extends undefined ? boolean : unknown extends T["param"] ? boolean : boolean | number | string | string[] : never;
};
declare class Flag<T extends TFlag = TFlag> {
    private _desc;
    private _char;
    private _param;
    private _preserveCase;
    private _negate;
    constructor(_desc: string, _char: T["char"], _param: T["param"], _preserveCase: T["preserveCase"], _negate: string | undefined);
    char<C extends string>(char: C): Flag<Replace<T, "char", C>>;
    requiredParam<U extends ParamType>(type: U): Flag<Replace<T, "param", RequiredParam<U>>>;
    optionalParam<U extends ParamType, D extends ParamTypeToValue<U> | undefined = undefined>(type: U, defaultValue?: D): Flag<Replace<T, "param", OptionalParam<U, D>>>;
    withNegated(description: string): Flag<Remove<T, "param">>;
    preserveCase(): Flag<Replace<T, "preserveCase", true>>;
    static toObject(flag: Flag): {
        desc: string;
        char: string | undefined;
        param: OptionalParam<any, any> | RequiredParam<any> | undefined;
        negate: string | undefined;
        preserveCase: boolean | undefined;
    };
    static toString(flag: Flag, name: string): string;
    static toNegatedString(flag: Flag, name: string): string;
    static isNegated(str: string): boolean;
    static toKebabCase(str: string): string;
    static toCamelCase(str: string): string;
}
declare function flag(description: string): Flag<{}>;

type SubcommandOptions<ProgramFlags extends Record<string, Flag>, Deps extends Record<string, any>> = {
    program: Sade;
    programFlags: ProgramFlags;
    baseCmd?: string | undefined;
    defaultCmd?: Subcommand<any, any>;
    container: IsEmptyObject<Deps> extends true ? HollywoodOf<any> | undefined : HollywoodOf<Deps>;
};
type Subcommand<ProgramFlags extends Record<string, Flag>, Deps extends Record<string, any>> = {
    command: (options: SubcommandOptions<ProgramFlags, Deps>) => void;
};
type CommandOptions<Name extends string, Args extends Record<string, Arg>, Flags extends Record<string, Flag>, ProgramFlags extends Record<string, Flag>, Deps extends Record<string, any>, Container extends AnyHollywood | undefined, Instances extends Container extends AnyHollywood ? InferContainer<Container> : Deps> = {
    name: Name;
    description: string | undefined;
    aliases: string[];
    examples: string[];
    args: Args;
    flags: Flags;
    action: (ctx: {
        args: Spread<ParsedArgs<Args>>;
        flags: Spread<ParsedFlags<Merge<Flags, ProgramFlags>>>;
        restArgs: string[];
    }, container: Instances) => any | Promise<any>;
    subcommands: [
        ({
            tokens: RegisterTokens<any, any>;
            options?: ContainerOptions;
        } | undefined),
        Subcommand<ProgramFlags, Instances>[]
    ][];
};
type AnyCommand = Command<any, any, any, any, any>;
declare class Command<Name extends string, Args extends Record<string, Arg>, Flags extends Record<string, Flag>, ProgramFlags extends Record<string, Flag>, Deps extends Record<string, any>> {
    private options;
    constructor(options: CommandOptions<Name, Args, Flags, ProgramFlags, Deps, any, any>);
    command(options: SubcommandOptions<ProgramFlags, Deps>): void;
}
declare class CommandBuilder<Name extends string, Args extends Record<string, Arg>, Flags extends Record<string, Flag>, ProgramFlags extends Record<string, Flag>, Deps extends Record<string, any>, Container extends AnyHollywood | undefined> {
    private options;
    constructor(name: Name);
    describe(description: string): this;
    alias(...aliases: string[]): this;
    example(example: string): this;
    args<A extends Record<string, Arg> extends Args ? Record<string, Arg> : never>(args: A): Record<string, Arg> extends Args ? CommandBuilder<Name, A, Flags, ProgramFlags, Deps, Container> : never;
    flags<F extends Record<string, Flag> extends Flags ? Record<string, Flag> : never>(flags: F): Record<string, Flag> extends Flags ? CommandBuilder<Name, Args, F, ProgramFlags, Deps, Container> : never;
    useFlags<F extends Record<string, Flag> extends ProgramFlags ? Record<string, Flag> : never>(): Record<string, Flag> extends ProgramFlags ? CommandBuilder<Name, Args, Flags, F, Deps, Container> : never;
    use<C extends Container extends AnyHollywood ? never : Record<string, any>>(): Container extends AnyHollywood ? never : CommandBuilder<Name, Args, Flags, ProgramFlags, Spread<C>, Hollywood<Spread<C>, any>>;
    provide<T extends Record<string, any> = {}>(tokens: RegisterTokens<T, Container extends AnyHollywood ? InferContainer<Container> : {}>, options?: ContainerOptions): CommandBuilder<Name, Args, Flags, ProgramFlags, Deps, Container extends AnyHollywood ? Hollywood<T, InferContainer<Container>> : Hollywood<T, {}>>;
    subcommands<C extends Subcommand<ProgramFlags, Container extends AnyHollywood ? InferContainer<Container> : Deps>[]>(...commands: C): this;
    action(fn: (ctx: {
        args: Spread<ParsedArgs<Args>>;
        flags: Spread<ParsedFlags<Merge<Flags, ProgramFlags>>>;
        restArgs: string[];
    }, container: Container extends AnyHollywood ? KnownMappedKeys<InferContainer<Container>> : {}) => any | Promise<any>): Command<Name, Args, Flags, ProgramFlags, Deps>;
}
declare function createCommand<Name extends string>(name: Name): CommandBuilder<Name, {}, {}, {}, {}, undefined>;

declare class CommandError extends Error {
    reason: "invalid_arg" | "invalid_flag";
    message: string;
    constructor(reason: "invalid_arg" | "invalid_flag", message: string);
}

type RunOptions = {
    errorOnUnknown?: boolean | ((flag: string) => string);
    onError?(err: any): void;
};
declare function run(cli: Sade, options?: RunOptions, argv?: string[]): Promise<any>;

type ProgramOptions<Name extends string, Flags extends Record<string, Flag>, Container extends AnyHollywood | undefined, Instances extends Container extends AnyHollywood ? InferContainer<Container> : {}> = {
    name: Name;
    version: string | undefined;
    description: string | undefined;
    examples: string[];
    flags: Flags;
    container: Container;
    commands: [HollywoodOf<Instances>, Subcommand<Flags, Instances>[]][];
    default: Subcommand<any, any> | undefined;
};
type AnyProgram = Program<any, any, any>;
declare class Program<Name extends string, Flags extends Record<string, Flag>, Container extends AnyHollywood | undefined> {
    private options;
    constructor(options: ProgramOptions<Name, Flags, Container, Container extends AnyHollywood ? InferContainer<Container> : {}>);
    program(): Sade;
    run(options?: RunOptions, argv?: string[]): Promise<any>;
}
declare class ProgramBuilder<Name extends string, Flags extends Record<string, Flag>, Container extends AnyHollywood | undefined> {
    private options;
    constructor(name: Name, container: Container);
    version(version: string): this;
    describe(description: string): this;
    example(example: string): this;
    flags<F extends Record<string, Flag> extends Flags ? Record<string, Flag> : never>(flags: F): Record<string, Flag> extends Flags ? ProgramBuilder<Name, F, Container> : never;
    provide<T extends Record<string, any> = {}>(tokens: RegisterTokens<T, Container extends AnyHollywood ? InferContainer<Container> : {}>, options?: ContainerOptions): ProgramBuilder<Name, Flags, Container extends AnyHollywood ? Hollywood<T, InferContainer<Container>> : Hollywood<T, {}>>;
    commands<C extends Subcommand<Flags, Container extends AnyHollywood ? InferContainer<Container> : {}>[]>(...commands: C): this;
    default(command: Subcommand<any, any>): this;
    build(): Program<Name, Flags, Container>;
}
declare function createProgram<Name extends string>(name: Name): ProgramBuilder<Name, {}, undefined>;
declare function createProgram<Name extends string, Container extends AnyHollywood>(name: Name, container: Container): ProgramBuilder<Name, {}, Container>;

type SingleProgramOptions<Name extends string, Args extends Record<string, Arg>, Flags extends Record<string, Flag>, Container extends AnyHollywood | undefined, Instances extends Container extends AnyHollywood ? InferContainer<Container> : {}> = {
    name: Name;
    version: string | undefined;
    description: string | undefined;
    examples: string[];
    args: Args;
    flags: Flags;
    container: Container;
    action: (ctx: {
        args: Spread<ParsedArgs<Args>>;
        flags: Spread<ParsedFlags<Flags>>;
        restArgs: string[];
    }, container: Instances) => any | Promise<any>;
};
type AnySingleProgram = SingleProgram<any, any, any, any>;
declare class SingleProgram<Name extends string, Args extends Record<string, Arg>, Flags extends Record<string, Flag>, Container extends AnyHollywood | undefined> {
    private options;
    constructor(options: SingleProgramOptions<Name, Args, Flags, Container, Container extends AnyHollywood ? InferContainer<Container> : {}>);
    program(): Sade;
    run(options?: RunOptions, argv?: string[]): Promise<any>;
}
declare class SingleProgramBuilder<Name extends string, Args extends Record<string, Arg>, Flags extends Record<string, Flag>, Container extends AnyHollywood | undefined> {
    private options;
    constructor(name: Name, container: Container);
    version(version: string): this;
    describe(description: string): this;
    example(example: string): this;
    args<A extends Record<string, Arg> extends Args ? Record<string, Arg> : never>(args: A): Record<string, Arg> extends Args ? SingleProgramBuilder<Name, A, Flags, Container> : never;
    flags<F extends Record<string, Flag> extends Flags ? Record<string, Flag> : never>(flags: F): Record<string, Flag> extends Flags ? SingleProgramBuilder<Name, Args, F, Container> : never;
    provide<T extends Record<string, any> = {}>(tokens: RegisterTokens<T, Container extends AnyHollywood ? InferContainer<Container> : {}>, options?: ContainerOptions): SingleProgramBuilder<Name, Args, Flags, Container extends AnyHollywood ? Hollywood<T, InferContainer<Container>> : Hollywood<T, {}>>;
    action(fn: (ctx: {
        args: Spread<ParsedArgs<Args>>;
        flags: Spread<ParsedFlags<Flags>>;
        restArgs: string[];
    }, container: Container extends AnyHollywood ? KnownMappedKeys<InferContainer<Container>> : {}) => any | Promise<any>): SingleProgram<Name, Args, Flags, Container>;
}
declare function createSingleProgram<Name extends string>(name: Name): SingleProgramBuilder<Name, {}, {}, undefined>;
declare function createSingleProgram<Name extends string, Container extends AnyHollywood>(name: Name, container: Container): SingleProgramBuilder<Name, {}, {}, Container>;

export { type AnyCommand, type AnyProgram, type AnySingleProgram, Arg, Command, CommandError, Flag, Program, type RunOptions, SingleProgram, arg, createCommand, createProgram, createSingleProgram, flag, run };
