type ClassTransformOptions = import('class-transformer').ClassTransformOptions;
type SinonStatic = import('sinon').SinonStatic;
type SinonSandbox = import('sinon').SinonSandbox;
type SinonStubbedInstance<T> = import('sinon').SinonStubbedInstance<T>;
type SelectQueryBuilder<T> = import('typeorm').SelectQueryBuilder<T>;

declare namespace TypeORMFaker {
    type CamelCaseToSnakeCase<
        T extends string,
        Joiner extends '' | '_' = ''
    > = T extends `${infer Character}${infer Rest}`
        ? Character extends Uppercase<Character>
            ? `${Joiner}${Lowercase<Character>}${CamelCaseToSnakeCase<Rest, '_'>}`
            : `${Character}${CamelCaseToSnakeCase<Rest, '_'>}`
        : '';

    type TypeORMRawColumns<
        Entity,
        EntityClassName extends string,
        AdditionalFields extends string,
        Joiner = EntityClassName extends '' ? '' : '_',
        ConvertedPropertyAsSnake = `${Uncapitalize<EntityClassName>}${Extract<
            Joiner,
            string
        >}${CamelCaseToSnakeCase<Extract<keyof Entity, string>>}`,
        ValueOf = Entity[keyof Entity]
    > = {
        [key in Extract<ConvertedPropertyAsSnake | AdditionalFields, string>]?: ValueOf;
    };

    export type StubStatic = <T>(
        EntityClass: new () => T,
        count?: number,
        options?: Partial<T> | undefined,
        classTransformOptions?: ClassTransformOptions
    ) => T[];

    export type StubOneStatic = <T>(
        EntityClass: new () => T,
        options?: Partial<T>,
        classTransformOptions?: ClassTransformOptions
    ) => T;

    export type StubRawStatic = <
        Entity,
        ClassName extends string,
        AdditionalFields = undefined,
        OptionalCalculatedAdditionalFields = [AdditionalFields] extends [undefined]
            ? never
            : AdditionalFields
    >(
        EntityClass: new () => Entity,
        count?: number,
        options?: Partial<TypeORMRawColumns<Entity, ClassName, OptionalCalculatedAdditionalFields>>
    ) => Array<TypeORMRawColumns<Entity, ClassName, OptionalCalculatedAdditionalFields>>;

    export type StubRawOneStatic = <
        Entity,
        ClassName extends string,
        AdditionalFields = undefined,
        OptionalCalculatedAdditionalFields = [AdditionalFields] extends [undefined]
            ? never
            : AdditionalFields
    >(
        EntityClass: new () => Entity,
        options?: Partial<TypeORMRawColumns<Entity, ClassName, OptionalCalculatedAdditionalFields>>
    ) => TypeORMRawColumns<Entity, ClassName, OptionalCalculatedAdditionalFields>;

    export type StubQueryBuilderStatic = <Entity>(
        sandbox: SinonStatic | sinon.SinonSandbox,
        EntityClass: new () => Entity,
        customStubOrStubs?: Entity | Entity[]
    ) => SinonStubbedInstance<SelectQueryBuilder<Entity>>;

    export interface TypeORMFakerStatic {
        stubOne: StubOneStatic;
        stub: StubStatic;
        stubRawOne: StubRawOneStatic;
        stubRaw: StubRawStatic;
        stubQueryBuilder: StubQueryBuilderStatic;
    }
}

declare const typeormFaker: TypeORMFaker.TypeORMFakerStatic;
declare const stub: TypeORMFaker.StubStatic;
declare const stubOne: TypeORMFaker.StubOneStatic;
declare const stubRaw: TypeORMFaker.StubRawStatic;
declare const stubRawOne: TypeORMFaker.StubRawOneStatic;
declare const stubQueryBuilder: TypeORMFaker.StubQueryBuilderStatic;

declare module 'typeorm-faker' {
    export = typeormFaker;
}
