import { AxiosRequestConfig, AxiosPromise } from 'axios';

/**
 * Check whether `A1` is equal to `A2` or not.
 * @param A1
 * @param A2
 * @returns [[Boolean]]
 * @example
 * ```ts
 * import {A} from 'ts-toolbelt'
 *
 * type test0 = A.Equals<42 | 0, 42 | 0>                    // true
 * type test1 = A.Equals<{a: string}, {b: string}>          // false
 * type test3 = A.Equals<{a: string}, {readonly a: string}> // false
 * ```
 */
declare type Equals<A1 extends any, A2 extends any> = (<A>() => A extends A2 ? 1 : 0) extends (<A>() => A extends A1 ? 1 : 0) ? 1 : 0;

/**
 * Describes index keys for any type
 */
declare type Key = string | number | symbol;

/**
 * A [[List]]
 * @param A its type
 * @returns [[List]]
 * @example
 * ```ts
 * type list0 = [1, 2, 3]
 * type list1 = number[]
 * ```
 */
declare type List<A = any> = ReadonlyArray<A>;

/**
 * Get in `O` the type of a field of key `K`
 * @param O to extract from
 * @param K to extract at
 * @returns [[Any]]
 * @example
 * ```ts
 * import {O} from 'ts-toolbelt'
 *
 * type User = {
 *  info: {
 *      name: string
 *      age: number
 *      payment: {}
 *  }
 *  id: number
 * }
 *
 * type test0 = O.At<User, 'id'> // number
 * ```
 */
declare type At<A extends any, K extends Key> = A extends List ? number extends A['length'] ? K extends number | `${number}` ? A[never] | undefined : undefined : K extends keyof A ? A[K] : undefined : unknown extends A ? unknown : K extends keyof A ? A[K] : undefined;

/**
 * Ask TS to re-check that `A1` extends `A2`.
 * And if it fails, `A2` will be enforced anyway.
 * Can also be used to add constraints on parameters.
 * @param A1 to check against
 * @param A2 to cast to
 * @returns `A1 | A2`
 * @example
 * ```ts
 * import {A} from 'ts-toolbelt'
 *
 * type test0 = A.Cast<'42', string> // '42'
 * type test1 = A.Cast<'42', number> // number
 * ```
 */
declare type Cast<A1 extends any, A2 extends any> = A1 extends A2 ? A1 : A2;

/**
 * Check whether `A1` is part of `A2` or not. The difference with
 * `extends` is that it forces a [[Boolean]] return.
 * @param A1
 * @param A2
 * @returns [[Boolean]]
 * @example
 * ```ts
 * import {A} from 'ts-toolbelt'
 *
 * type test0 = A.Extends<'a' | 'b', 'b'> // Boolean
 * type test1 = A.Extends<'a', 'a' | 'b'> // True
 *
 * type test2 = A.Extends<{a: string}, {a: any}>      // True
 * type test3 = A.Extends<{a: any}, {a: any, b: any}> // False
 *
 * type test4 = A.Extends<never, never> // False
 * /// Nothing cannot extend nothing, use `A.Equals`
 * ```
 */
declare type Extends<A1 extends any, A2 extends any> = [
    A1
] extends [never] ? 0 : A1 extends A2 ? 1 : 0;

/**
 * Check whether `A1` is part of `A2` or not. It works like
 * [[Extends]] but [[Boolean]] results are narrowed to [[False]].
 * @param A1
 * @param A2
 * @returns [[Boolean]]
 * @example
 * ```ts
 * type test0 = A.Contains<'a' | 'b', 'b'> // False
 * type test1 = A.Contains<'a', 'a' | 'b'> // True
 *
 * type test2 = A.Contains<{a: string}, {a: string, b: number}> // False
 * type test3 = A.Contains<{a: string, b: number}, {a: string}> // True
 *
 * type test4 = A.Contains<never, never> // False
 * /// Nothing cannot contain nothing, use `A.Equals`
 * ```
 */
declare type Contains<A1 extends any, A2 extends any> = Extends<A1, A2> extends 1 ? 1 : 0;

/**
 * Describes the match strategy when matching types
 * * `default`     : `extends->`
 * * `contains->`  : X contains   Y ([[Contains]]<X, Y>)
 * * `extends->`   : X extends    Y ([[Extends]]<X, Y>)
 * * `<-contains`  : Y contains   X ([[Contains]]<Y, X>)
 * * `<-extends`   : Y extends    X ([[Extends]]<Y, X>)
 * * `equals`      : X equals     Y (([[Equals]]<X, Y>))
 */
declare type Match = 'default' | 'contains->' | 'extends->' | '<-contains' | '<-extends' | 'equals';

/**
 * Check whether `A` is similar to `A1` or not. In other words, it is a compact
 * type that bundles [[Equals]], [[Extends]], [[Contains]], comparison types.
 * @param A to be compared
 * @param A1 to compare to
 * @param match (?=`'default'`) to change precision
 * @returns [[Boolean]]
 * @example
 * ```ts
 * import {A} from 'ts-toolbelt'
 *
 * type test0 = A.Is<'a', 'a' | 'b', 'extends->'> // True
 * type test1 = A.Is<'a' | 'b', 'a', 'extends->'> // Boolean
 *
 * type test2 = A.Is<'a', 'a' | 'b', '<-extends'> // Boolean
 * type test3 = A.Is<'a' | 'b', 'a', '<-extends'> // True
 *
 * type test4 = A.Is<'a', 'a' | 'b', 'contains->'> // True
 * type test5 = A.Is<'a' | 'b', 'a', 'contains->'> // False
 *
 * type test6 = A.Is<'a', 'a' | 'b', '<-contains'> // False
 * type test7 = A.Is<'a' | 'b', 'a', '<-contains'> // True
 *
 * type test8 = A.Is<'a', 'a' | 'b', 'equals'>      // False
 * type test9 = A.Is<'b' |'a', 'a' | 'b', 'equals'> // True
 * ```
 */
declare type Is<A extends any, A1 extends any, match extends Match = 'default'> = {
    'default': Extends<A, A1>;
    'contains->': Contains<A, A1>;
    'extends->': Extends<A, A1>;
    '<-contains': Contains<A1, A>;
    '<-extends': Extends<A1, A>;
    'equals': Equals<A1, A>;
}[match];

/**
 * @hidden
 */
declare type _UnionOf<O extends object> = O[keyof O];
/**
 * Transform an [[Object]] into an [[Union]]
 * @param O to transform
 * @returns [[Any]]
 * @example
 * ```ts
 * ```
 */
declare type UnionOf<O extends object> = O extends unknown ? _UnionOf<O> : never;

/**
 * An entry of `IterationMap`
 */
declare type Iteration = [
    value: number,
    sign: '-' | '0' | '+',
    prev: keyof IterationMap,
    next: keyof IterationMap,
    oppo: keyof IterationMap
];
declare type IterationMap = {
    '__': [number, '-' | '0' | '+', '__', '__', '__'];
    '-100': [-100, '-', '__', '-99', '100'];
    '-99': [-99, '-', '-100', '-98', '99'];
    '-98': [-98, '-', '-99', '-97', '98'];
    '-97': [-97, '-', '-98', '-96', '97'];
    '-96': [-96, '-', '-97', '-95', '96'];
    '-95': [-95, '-', '-96', '-94', '95'];
    '-94': [-94, '-', '-95', '-93', '94'];
    '-93': [-93, '-', '-94', '-92', '93'];
    '-92': [-92, '-', '-93', '-91', '92'];
    '-91': [-91, '-', '-92', '-90', '91'];
    '-90': [-90, '-', '-91', '-89', '90'];
    '-89': [-89, '-', '-90', '-88', '89'];
    '-88': [-88, '-', '-89', '-87', '88'];
    '-87': [-87, '-', '-88', '-86', '87'];
    '-86': [-86, '-', '-87', '-85', '86'];
    '-85': [-85, '-', '-86', '-84', '85'];
    '-84': [-84, '-', '-85', '-83', '84'];
    '-83': [-83, '-', '-84', '-82', '83'];
    '-82': [-82, '-', '-83', '-81', '82'];
    '-81': [-81, '-', '-82', '-80', '81'];
    '-80': [-80, '-', '-81', '-79', '80'];
    '-79': [-79, '-', '-80', '-78', '79'];
    '-78': [-78, '-', '-79', '-77', '78'];
    '-77': [-77, '-', '-78', '-76', '77'];
    '-76': [-76, '-', '-77', '-75', '76'];
    '-75': [-75, '-', '-76', '-74', '75'];
    '-74': [-74, '-', '-75', '-73', '74'];
    '-73': [-73, '-', '-74', '-72', '73'];
    '-72': [-72, '-', '-73', '-71', '72'];
    '-71': [-71, '-', '-72', '-70', '71'];
    '-70': [-70, '-', '-71', '-69', '70'];
    '-69': [-69, '-', '-70', '-68', '69'];
    '-68': [-68, '-', '-69', '-67', '68'];
    '-67': [-67, '-', '-68', '-66', '67'];
    '-66': [-66, '-', '-67', '-65', '66'];
    '-65': [-65, '-', '-66', '-64', '65'];
    '-64': [-64, '-', '-65', '-63', '64'];
    '-63': [-63, '-', '-64', '-62', '63'];
    '-62': [-62, '-', '-63', '-61', '62'];
    '-61': [-61, '-', '-62', '-60', '61'];
    '-60': [-60, '-', '-61', '-59', '60'];
    '-59': [-59, '-', '-60', '-58', '59'];
    '-58': [-58, '-', '-59', '-57', '58'];
    '-57': [-57, '-', '-58', '-56', '57'];
    '-56': [-56, '-', '-57', '-55', '56'];
    '-55': [-55, '-', '-56', '-54', '55'];
    '-54': [-54, '-', '-55', '-53', '54'];
    '-53': [-53, '-', '-54', '-52', '53'];
    '-52': [-52, '-', '-53', '-51', '52'];
    '-51': [-51, '-', '-52', '-50', '51'];
    '-50': [-50, '-', '-51', '-49', '50'];
    '-49': [-49, '-', '-50', '-48', '49'];
    '-48': [-48, '-', '-49', '-47', '48'];
    '-47': [-47, '-', '-48', '-46', '47'];
    '-46': [-46, '-', '-47', '-45', '46'];
    '-45': [-45, '-', '-46', '-44', '45'];
    '-44': [-44, '-', '-45', '-43', '44'];
    '-43': [-43, '-', '-44', '-42', '43'];
    '-42': [-42, '-', '-43', '-41', '42'];
    '-41': [-41, '-', '-42', '-40', '41'];
    '-40': [-40, '-', '-41', '-39', '40'];
    '-39': [-39, '-', '-40', '-38', '39'];
    '-38': [-38, '-', '-39', '-37', '38'];
    '-37': [-37, '-', '-38', '-36', '37'];
    '-36': [-36, '-', '-37', '-35', '36'];
    '-35': [-35, '-', '-36', '-34', '35'];
    '-34': [-34, '-', '-35', '-33', '34'];
    '-33': [-33, '-', '-34', '-32', '33'];
    '-32': [-32, '-', '-33', '-31', '32'];
    '-31': [-31, '-', '-32', '-30', '31'];
    '-30': [-30, '-', '-31', '-29', '30'];
    '-29': [-29, '-', '-30', '-28', '29'];
    '-28': [-28, '-', '-29', '-27', '28'];
    '-27': [-27, '-', '-28', '-26', '27'];
    '-26': [-26, '-', '-27', '-25', '26'];
    '-25': [-25, '-', '-26', '-24', '25'];
    '-24': [-24, '-', '-25', '-23', '24'];
    '-23': [-23, '-', '-24', '-22', '23'];
    '-22': [-22, '-', '-23', '-21', '22'];
    '-21': [-21, '-', '-22', '-20', '21'];
    '-20': [-20, '-', '-21', '-19', '20'];
    '-19': [-19, '-', '-20', '-18', '19'];
    '-18': [-18, '-', '-19', '-17', '18'];
    '-17': [-17, '-', '-18', '-16', '17'];
    '-16': [-16, '-', '-17', '-15', '16'];
    '-15': [-15, '-', '-16', '-14', '15'];
    '-14': [-14, '-', '-15', '-13', '14'];
    '-13': [-13, '-', '-14', '-12', '13'];
    '-12': [-12, '-', '-13', '-11', '12'];
    '-11': [-11, '-', '-12', '-10', '11'];
    '-10': [-10, '-', '-11', '-9', '10'];
    '-9': [-9, '-', '-10', '-8', '9'];
    '-8': [-8, '-', '-9', '-7', '8'];
    '-7': [-7, '-', '-8', '-6', '7'];
    '-6': [-6, '-', '-7', '-5', '6'];
    '-5': [-5, '-', '-6', '-4', '5'];
    '-4': [-4, '-', '-5', '-3', '4'];
    '-3': [-3, '-', '-4', '-2', '3'];
    '-2': [-2, '-', '-3', '-1', '2'];
    '-1': [-1, '-', '-2', '0', '1'];
    '0': [0, '0', '-1', '1', '0'];
    '1': [1, '+', '0', '2', '-1'];
    '2': [2, '+', '1', '3', '-2'];
    '3': [3, '+', '2', '4', '-3'];
    '4': [4, '+', '3', '5', '-4'];
    '5': [5, '+', '4', '6', '-5'];
    '6': [6, '+', '5', '7', '-6'];
    '7': [7, '+', '6', '8', '-7'];
    '8': [8, '+', '7', '9', '-8'];
    '9': [9, '+', '8', '10', '-9'];
    '10': [10, '+', '9', '11', '-10'];
    '11': [11, '+', '10', '12', '-11'];
    '12': [12, '+', '11', '13', '-12'];
    '13': [13, '+', '12', '14', '-13'];
    '14': [14, '+', '13', '15', '-14'];
    '15': [15, '+', '14', '16', '-15'];
    '16': [16, '+', '15', '17', '-16'];
    '17': [17, '+', '16', '18', '-17'];
    '18': [18, '+', '17', '19', '-18'];
    '19': [19, '+', '18', '20', '-19'];
    '20': [20, '+', '19', '21', '-20'];
    '21': [21, '+', '20', '22', '-21'];
    '22': [22, '+', '21', '23', '-22'];
    '23': [23, '+', '22', '24', '-23'];
    '24': [24, '+', '23', '25', '-24'];
    '25': [25, '+', '24', '26', '-25'];
    '26': [26, '+', '25', '27', '-26'];
    '27': [27, '+', '26', '28', '-27'];
    '28': [28, '+', '27', '29', '-28'];
    '29': [29, '+', '28', '30', '-29'];
    '30': [30, '+', '29', '31', '-30'];
    '31': [31, '+', '30', '32', '-31'];
    '32': [32, '+', '31', '33', '-32'];
    '33': [33, '+', '32', '34', '-33'];
    '34': [34, '+', '33', '35', '-34'];
    '35': [35, '+', '34', '36', '-35'];
    '36': [36, '+', '35', '37', '-36'];
    '37': [37, '+', '36', '38', '-37'];
    '38': [38, '+', '37', '39', '-38'];
    '39': [39, '+', '38', '40', '-39'];
    '40': [40, '+', '39', '41', '-40'];
    '41': [41, '+', '40', '42', '-41'];
    '42': [42, '+', '41', '43', '-42'];
    '43': [43, '+', '42', '44', '-43'];
    '44': [44, '+', '43', '45', '-44'];
    '45': [45, '+', '44', '46', '-45'];
    '46': [46, '+', '45', '47', '-46'];
    '47': [47, '+', '46', '48', '-47'];
    '48': [48, '+', '47', '49', '-48'];
    '49': [49, '+', '48', '50', '-49'];
    '50': [50, '+', '49', '51', '-50'];
    '51': [51, '+', '50', '52', '-51'];
    '52': [52, '+', '51', '53', '-52'];
    '53': [53, '+', '52', '54', '-53'];
    '54': [54, '+', '53', '55', '-54'];
    '55': [55, '+', '54', '56', '-55'];
    '56': [56, '+', '55', '57', '-56'];
    '57': [57, '+', '56', '58', '-57'];
    '58': [58, '+', '57', '59', '-58'];
    '59': [59, '+', '58', '60', '-59'];
    '60': [60, '+', '59', '61', '-60'];
    '61': [61, '+', '60', '62', '-61'];
    '62': [62, '+', '61', '63', '-62'];
    '63': [63, '+', '62', '64', '-63'];
    '64': [64, '+', '63', '65', '-64'];
    '65': [65, '+', '64', '66', '-65'];
    '66': [66, '+', '65', '67', '-66'];
    '67': [67, '+', '66', '68', '-67'];
    '68': [68, '+', '67', '69', '-68'];
    '69': [69, '+', '68', '70', '-69'];
    '70': [70, '+', '69', '71', '-70'];
    '71': [71, '+', '70', '72', '-71'];
    '72': [72, '+', '71', '73', '-72'];
    '73': [73, '+', '72', '74', '-73'];
    '74': [74, '+', '73', '75', '-74'];
    '75': [75, '+', '74', '76', '-75'];
    '76': [76, '+', '75', '77', '-76'];
    '77': [77, '+', '76', '78', '-77'];
    '78': [78, '+', '77', '79', '-78'];
    '79': [79, '+', '78', '80', '-79'];
    '80': [80, '+', '79', '81', '-80'];
    '81': [81, '+', '80', '82', '-81'];
    '82': [82, '+', '81', '83', '-82'];
    '83': [83, '+', '82', '84', '-83'];
    '84': [84, '+', '83', '85', '-84'];
    '85': [85, '+', '84', '86', '-85'];
    '86': [86, '+', '85', '87', '-86'];
    '87': [87, '+', '86', '88', '-87'];
    '88': [88, '+', '87', '89', '-88'];
    '89': [89, '+', '88', '90', '-89'];
    '90': [90, '+', '89', '91', '-90'];
    '91': [91, '+', '90', '92', '-91'];
    '92': [92, '+', '91', '93', '-92'];
    '93': [93, '+', '92', '94', '-93'];
    '94': [94, '+', '93', '95', '-94'];
    '95': [95, '+', '94', '96', '-95'];
    '96': [96, '+', '95', '97', '-96'];
    '97': [97, '+', '96', '98', '-97'];
    '98': [98, '+', '97', '99', '-98'];
    '99': [99, '+', '98', '100', '-99'];
    '100': [100, '+', '99', '__', '-100'];
};

/**
 * Move `I`'s position forward
 * @param I to move
 * @returns [[Iteration]]
 * @example
 * ```ts
 * import {I} from 'ts-toolbelt'
 *
 * type i = I.IterationOf<'20'>
 *
 * type test0 = I.Pos<i>         // 20
 * type test1 = I.Pos<I.Next<i>> // 21
 * ```
 */
declare type Next<I extends Iteration> = IterationMap[I[3]];

/**
 * Transform a number into an [[Iteration]]
 * (to use [[Prev]], [[Next]], & [[Pos]])
 * @param N to transform
 * @returns [[Iteration]]
 * @example
 * ```ts
 * import {I} from 'ts-toolbelt'
 *
 * type i = I.IterationOf<0> // ["-1", "1", "0", 0, "0"]
 *
 * type next = I.Next<i>       // ["0", "2", "1", 1, "+"]
 * type prev = I.Prev<i>       // ["-2", "0", "-1", -1, "-"]
 *
 * type nnext = I.Pos<next>    // +1
 * type nprev = I.Pos<prev>    // -1
 * ```
 */
declare type IterationOf<N extends number> = `${N}` extends keyof IterationMap ? IterationMap[`${N}`] : IterationMap['__'];

/**
 * Get the position of `I` (**number**)
 * @param I to query
 * @returns `number`
 * @example
 * ```ts
 * import {I} from 'ts-toolbelt'
 *
 * type i = I.IterationOf<'20'>
 *
 * type test0 = I.Pos<i>         // 20
 * type test1 = I.Pos<I.Next<i>> // 21
 * ```
 */
declare type Pos<I extends Iteration> = I[0];

/**
 * Get the length of `L`
 * @param L to get length
 * @returns [[String]] or `number`
 * @example
 * ```ts
 * ```
 */
declare type Length<L extends List> = L['length'];

/**
 * Get the first entry of `L`
 * @param L to extract from
 * @returns [[Any]]
 * @example
 * ```ts
 * ```
 */
declare type Head<L extends List> = Length<L> extends 0 ? never : L[0];

/**
 * Remove the last element out of `L`
 * @param L to remove from
 * @returns [[List]]
 * @example
 * ```ts
 * ```
 */
declare type Pop<L extends List> = L extends (readonly [...infer LBody, any] | readonly [...infer LBody, any?]) ? LBody : L;

/**
 * Remove the first item out of a [[List]]
 * @param L
 * @returns [[List]]
 * @example
 * ```ts
 * ```
 */
declare type Tail<L extends List> = L extends readonly [] ? L : L extends readonly [any?, ...infer LTail] ? LTail : L;

/**
 * Extract the part of `U` that matches `M`
 * @param U to extract from
 * @param M to select with
 * @returns [[Union]]
 * @example
 * ```ts
 * ```
 */
declare type Select<U extends any, M extends any, match extends Match = 'default'> = U extends unknown ? {
    1: U & M;
    0: never;
}[Is<U, M, match>] : never;

declare type Literal = string | number | bigint | boolean;

/**
 * @hidden
 */
declare type _Join<T extends List, D extends string> = T extends [] ? '' : T extends [Literal] ? `${T[0]}` : T extends [Literal, ...infer R] ? `${T[0]}${D}${_Join<R, D>}` : string;
/**
 * Concat many literals together
 * @param T to concat
 * @param D to delimit
 */
declare type Join<T extends List<Literal>, D extends string = ''> = _Join<T, D> extends infer X ? Cast<X, string> : never;

/**
 * @ignore
 */
declare type __Split<S extends string, D extends string, T extends string[] = []> = S extends `${infer BS}${D}${infer AS}` ? __Split<AS, D, [...T, BS]> : [...T, S];
/**
 * @hidden
 */
declare type _Split<S extends string, D extends string = ''> = D extends '' ? Pop<__Split<S, D>> : __Split<S, D>;
/**
 * Split `S` by `D` into a [[List]]
 * @param S to split up
 * @param D to split at
 */
declare type Split<S extends string, D extends string = ''> = _Split<S, D> extends infer X ? Cast<X, string[]> : never;

/**
 * All primitive types
 */
declare type Primitive = boolean | string | number | bigint | symbol | undefined | null;

interface Options extends AxiosRequestConfig {
    queryMethod?: 'url' | 'body';
}
interface Stringifiable {
    toApicalypseString(): string;
}
interface Builder<T = any> extends Stringifiable {
    queryFields: {
        fields?: string;
        exclude?: string;
        sort?: string;
        limit?: string;
        offset?: string;
        search?: string;
        where: string[];
    };
    queryEndpoint?: string;
    queryName?: string;
}
interface NamedBuilder<T, N extends string> extends Omit<Builder<T>, 'queryEndpoint' | 'queryName'> {
    queryEndpoint: string;
    queryName: N;
}
interface NarrowBuilder<T> extends Builder<T> {
    __narrow: true;
}
interface CountBuilder<T> extends Builder<T> {
    __count: true;
}
type PickWith<T, K extends keyof T, X> = X extends keyof T ? Pick<T, K | X> : Pick<T, K>;
interface Pipe<T extends object, mode extends 'result' | 'count' = 'result'> {
    <A>(...steps: (BuilderOperator<T, T> | BuilderOperatorNarrow<T, A>)[]): Stringifiable & Executor<FallbackIfUnknown<A, PickWith<T, never, 'id'>>, mode>;
}
type PipeSub<T extends object, Ret extends string, mode extends 'result' | 'count' = 'result'> = {
    <A>(...steps: (BuilderOperator<T, T> | BuilderOperatorNarrow<T, A>)[]): NamedBuilder<FallbackIfUnknown<A, PickWith<T, never, 'id'>>, Ret> & (mode extends 'count' ? CountBuilder<any> : {});
};
interface Executor<T, mode extends 'result' | 'count' = 'result'> {
    execute(url: string, options?: Options): AxiosPromise<{
        result: T[];
        count: {
            count: number;
        };
    }[mode]>;
}
type ResultMultiMono<T extends NamedBuilder<any, any>> = {
    name: T["queryName"];
} & (T extends CountBuilder<any> ? {
    count: number;
} : {
    result: T extends NamedBuilder<infer S, any> ? S[] : never;
});
interface ExecutorMulti<T extends Builder<any>[]> {
    execute(url: string, options?: Options): AxiosPromise<T extends (infer S)[] ? ResultMultiMono<S extends NamedBuilder<any, any> ? S : never>[] : never>;
}
interface BuilderOperator<T, R> {
    (builder: Builder<T>): Builder<R>;
}
interface BuilderOperatorNarrow<T, R> {
    (builder: Builder<T>): NarrowBuilder<R>;
}
interface NamedBuilderOperator<T, N extends string> {
    (builder: Builder<T>): NamedBuilder<T, N>;
}
type StandardOperators = '=' | '!=';
type StringOperators = StandardOperators | '~';
type NumbersOperatos = StandardOperators | '>=' | '>' | '<=' | '<';
type AllowedValues = true | false | null;
type GetOp<T> = T extends number ? NumbersOperatos : T extends string ? StringOperators : StandardOperators;
declare enum WhereFlags {
    RAW = 1,
    NUMBER = 1,
    STRING = 2,
    STARTSWITH = 6,
    ENDSWITH = 10,
    CONTAINS = 14
}
declare enum WhereInFlags {
    AND = 32,
    NAND = 48,
    OR = 64,
    NOR = 80,
    EXACT = 128
}
type FallbackIfUnknown<T, F> = unknown extends T ? F : T;
type OuterKeyCast<T, K extends string> = PickOuterKey<K> & UnionKeyOf<T>;
type UnionKeyOf<T> = T extends Array<infer T> ? keyof T : T extends infer T ? keyof T : never;
type DeepPick<T, P extends string> = T extends Primitive ? T : T extends Array<infer I> ? DeepPick<I, P>[] : T extends object ? string extends P ? number : PickWith<InnerPick<T, P>, OuterKeyCast<T, P>, 'id'> : never;
type InnerPick<T, K extends string> = {
    [key in keyof T]: DeepPick<T[key], InnerKey<Extract<key, string>, K>>;
};
type FlatPath<O extends any, P extends List<Key>, X = Path<O, P>> = X extends object ? number : X;
type PickOuterKey<K extends string> = K extends '*' ? string : KeyHead<K>;
type KeyHead<K extends string> = K extends `${infer K}.${string}` ? K : K;
type InnerKey<key extends string, K> = [
    Extract<K, `${key}.${string}`>
] extends [`${key}.${infer K}`] ? K : never;
type FlatKey<O, K extends keyof O> = Exclude<O[K], null | undefined> extends Array<infer I> ? I : O[K];
type Flat<O> = Exclude<O, null | undefined> extends Array<infer I> ? I : O;
type _ExcludePrimitiveKeys<O> = O extends Primitive ? Omit<O, keyof O> : O;
type _Path<O, P extends List<Key>, It extends Iteration = IterationOf<0>> = {
    0: _Path<Flat<At<_ExcludePrimitiveKeys<O>, P[Pos<It>]>>, P, Next<It>>;
    1: O;
}[Extends<Pos<It>, Length<P>>];
type Path<O extends any, P extends List<Key>> = _Path<O, P> extends infer X ? Cast<X, any> : never;
type Index = number | string;
type KeyToIndex<K extends Key, SP extends List<Index>> = number extends K ? Head<SP> : K & Index;
type MetaPath<O, D extends string, St extends string, SP extends List<Index> = [], P extends List<Index> = []> = {
    [K in keyof Required<O>]: Exclude<MetaPath<FlatKey<O, K>, D, St, Tail<SP>, [...P, KeyToIndex<K, SP>]>, string> | Join<[...P, KeyToIndex<K, SP>], D> | ([St] extends [never] ? never : Join<[...P, St], D>);
};
type NextPath<OP> = Select<UnionOf<Exclude<OP, string> & {}>, string>;
type CurrentPath<OP> = Select<Exclude<OP, object>, string>;
type ExecPath<A, SP extends List<Index>, D extends string, St extends string> = NextPath<Path<MetaPath<A, D, St, SP>, SP>>;
type HintPath<A, P extends string, SP extends List<Index>, Exec extends string, D extends string, St extends string> = [Exec] extends [never] ? CurrentPath<Path<MetaPath<A, D, St, SP>, SP>> extends never ? ExecPath<A, Pop<SP>, D, St> : CurrentPath<Path<MetaPath<A, D, St, SP>, SP>> : Exec | P;
type _AutoPath<A, P extends string, D extends string, St extends string, SP extends List<Index> = Split<P, D>> = HintPath<A, P, SP, ExecPath<A, SP, D, St>, D, St>;
type AutoPath<O extends any, P extends string, D extends string = '.', St extends string = '*'> = _AutoPath<O, P, D, St>;
type AllAutoPath<O extends object, P extends List<string>> = {
    [K in keyof P]: AutoPath<O, P[K] & string>;
};
type NonEmptyStringList = [string, ...string[]];
type ChosenPaths<P extends NonEmptyStringList> = [string] extends P ? '*' : P[number];

/**
 * Select only given fields.
 * Resulting response type is narrowed to only include specified fields (including nested ones).
 *
 * @example
 * ```ts
 * request().pipe(
 *   fields('*'),                   // select all fields
 *   fields(["name", "genres.id"]), // select specific fields, incuding sub-type
 *   fields(["name", "genres.*"]),  // select specific fields, incuding all fields of sub-type
 * )
 * ```
 * @see {@link https://api-docs.igdb.com/?shell#fields}
 * @param f
 */
declare function fields<T extends Record<any, any>, P extends NonEmptyStringList>(f: AllAutoPath<T, P> | '*'): BuilderOperatorNarrow<T, DeepPick<T, ChosenPaths<P>>>;
/**
 * Exclude given fields from selection.
 * Resulting response type is narrowed to exclude specified fields (not compatible with nested fields).
 *
 * @example
 * ```ts
 * request().pipe(
 *   fields('*'),
 *   exclude(["name", "created_at"]), // exclude specific fields
 * )
 * ```
 * @see {@link https://api-docs.igdb.com/?shell#exclude}
 * @param exclude
 */
declare function exclude<T extends Record<any, any>, K extends keyof T>(exclude: K[]): BuilderOperatorNarrow<T, Omit<T, K>>;
/**
 * Sort results by specified field.
 *
 * @example
 * ```ts
 * request().pipe(
 *   sort('name'),          // defaults to 'asc' when direction is not specified
 *   sort('name', 'desc'),  // manually specify direction
 * )
 * ```
 * @see {@link https://api-docs.igdb.com/?shell#sorting}
 * @param field
 * @param direction
 */
declare function sort<T extends Record<any, any>>(field: keyof T, direction?: 'asc' | 'desc'): BuilderOperator<T, T>;
/**
 * Limit the number of results returned by the query.
 *
 * @example
 * ```ts
 * request().pipe(
 *   limit(42), // Only retrieve 42 elements
 * )
 * ```
 * @see {@link https://api-docs.igdb.com/?shell#pagination}
 * @param limit
 */
declare function limit<T extends Record<any, any>>(limit: number): BuilderOperator<T, T>;
/**
 * Start results at a given offset.
 *
 * @example
 * ```ts
 * request().pipe(
 *   offset(50), // Start he results at position 50...
 *   limit(42),  // ...and limit the number of results to 42
 * )
 * ```
 * @see {@link https://api-docs.igdb.com/?shell#pagination}
 * @param offset
 */
declare function offset<T extends Record<any, any>>(offset: number): BuilderOperator<T, T>;
/**
 * Search based on name, results are sorted by similarity to the given search string.
 *
 * @example
 * ```ts
 * request().pipe(
 *   search('zelda'),
 * )
 * ```
 * @see {@link https://api-docs.igdb.com/?shell#search-1}
 * @param search
 */
declare function search<T extends Record<any, any>>(search: string): BuilderOperator<T, T>;
/**
 * Filters results based on given parameters. All parameters are type validated if `request` is given a type.
 * Also see {@link whereIn}.
 *
 * @example
 * ```ts
 * request<MyType>().pipe(
 *   where('ratings', '>', 4),  // ratings greater than 4
 *   where('ratings', '>=', 4), // ratings greater than or equal 4
 *   where('ratings', '<', 4),  // ratings less than 4
 *   where('ratings', '<=', 4), // ratings less than or equal 4
 *   where('name', '=', 'zelda'),   // name is zelda (case sensitive)
 *   where('name', '~', 'zelda'),   // name is zelda (case insensitive)
 *   where('name', '!=', 'zelda'),  // name is not zelda
 *   where('name', '=', 'zelda', WhereFlags.STARTSWITH),  // name starts with zelda (also works with ~)
 *   where('name', '=', 'zelda', WhereFlags.ENDSWITH),    // name ends with zelda (also works with ~)
 *   where('name', '=', 'zelda', WhereFlags.CONTAINS),    // name contains zelda (also works with ~)
 *   where('release_date', '=', null)   // no release date
 *   where('release_date', '!=', null)  // release date has any value
 * )
 * ```
 * @see {@link https://api-docs.igdb.com/?shell#filters}
 * @param key
 * @param op
 * @param value
 * @param flag
 */
declare function where<T extends Record<any, any>, P extends string>(key: AutoPath<T, P, '.', never>, op: GetOp<Path<T, Split<P, '.'>>>, value: FlatPath<T, Split<P, '.'>> | AllowedValues, flag?: WhereFlags): BuilderOperator<T, T>;
/**
 * Filters results based on given parameters. All parameters are type validated if `request` is given a type.
 * Also see {@link where}.
 *
 * @example
 * ```ts
 * request<MyType>().pipe(
 *   whereIn('genres', [1, 2], WhereInFlags.AND),   // Results whose genres includes 1 and 2
 *   whereIn('genres', [1, 2], WhereInFlags.OR),    // Results whose genres includes 1 or 2
 *   whereIn('genres', [1, 2], WhereInFlags.NAND),  // Results whose genres does not contain both 1 and 2, but can be 1 or 2
 *   whereIn('genres', [1, 2], WhereInFlags.NOR),   // Results whose genres does not contain 1 or does not contain 2
 *   whereIn('genres', [1, 2], WhereInFlags.EXACT), // Results whose exclusive genres are 1 and 2
 * )
 * ```
 * @see {@link https://api-docs.igdb.com/?shell#filters}
 * @param key
 * @param values
 * @param flag
 */
declare function whereIn<T extends Record<any, any>, P extends string>(key: AutoPath<T, P, '.', never>, values: FlatPath<T, Split<P, '.'>>[], flag?: WhereInFlags | WhereFlags): BuilderOperator<T, T>;
/**
 * Combine multiple {@link where} and {@link whereIn} conditions.
 *
 * @example
 * ```ts
 * // Select results that are on platform 1 and 2, and whose name are either 'zelda' or 'link'
 * request<MyType>().pipe(
 *   and(
 *     whereIn('platforms', [1, 2], WhereInFlags.AND),
 *     or(
 *       where('name', '=', 'zelda'),
 *       where('name', '=', 'link'),
 *     )
 *   )
 * )
 * ```
 * @see {@link https://api-docs.igdb.com/?shell#filters}
 * @param operators
 */
declare function and<T extends Record<any, any>>(...operators: BuilderOperator<T, T>[]): BuilderOperator<T, T>;
/**
 * Combine multiple {@link where} and {@link whereIn} conditions.
 *
 * @example
 * ```ts
 * // Select results that are on platform 1 and 2, and whose name are either 'zelda' or 'link'
 * request<MyType>().pipe(
 *   and(
 *     whereIn('platforms', [1, 2], WhereInFlags.AND),
 *     or(
 *       where('name', '=', 'zelda'),
 *       where('name', '=', 'link'),
 *     )
 *   )
 * )
 * ```
 * @see {@link https://api-docs.igdb.com/?shell#filters}
 * @param operators
 */
declare function or<T extends Record<any, any>, R>(...operators: BuilderOperator<T, R>[]): BuilderOperator<T, R>;

/**
 * Prepare a request to an apicalypse endpoint.
 *
 * @example
 * ```ts
 * // MyType is optional but recommended, so that results are properly typed
 * request<MyType>().pipe(
 *   fields(["name"]),
 *   sort("created_at", "asc"),
 *   where("created_at", ">",  now),
 * )
 * .execute('http:...')
 * .then(results => ...);
 * ```
 */
declare function request<T extends Record<any, any>, mode extends 'result' | 'count' = 'result'>(): {
    pipe: Pipe<T, mode>;
    sub: <S extends string>(queryEndpoint: string, queryName: S) => {
        pipe: PipeSub<T, S, mode>;
    };
};
/**
 * Prepare a multi-query request to an apicalypse endpoint.
 *
 * @example
 * ```ts
 * multi(
 *   // MyType is optional but recommended, so that results are properly typed
 *   request<MyType>()
 *     .sub('endpoint', 'alias') // mandatory for multi requests
 *     .pipe(
 *       fields(["name"]),
 *       sort("created_at", "asc"),
 *       where("created_at", ">",  now),
 *     ),
 *   request<MyType>()
 *     .sub('endpoint', 'alias2') // mandatory for multi requests
 *     .pipe(
 *       fields(["name"]),
 *       sort("created_at", "asc"),
 *       where("created_at", "<",  now),
 *     )
 * )
 * .execute('http:...')
 * .then(response => ...);
 * ```
 * @see {@link https://api-docs.igdb.com/?shell#multi-query}
 * @param builders
 */
declare function multi<T extends Record<any, any>, B extends Builder<T>[]>(...builders: B): Stringifiable & ExecutorMulti<B>;
/**
 * Narrow multi-query results types.
 *
 * @example
 * ```ts
 * interface MyType {
 *   name: string,
 *   created_at: number
 * }
 *
 * multi(
 *  request<MyType>().sub("games", "latest-games").pipe(
 *    fields(["name"]),
 *    sort("created_at", "desc"),
 *    where("created_at", "<",  now),
 *  ),
 *  request<MyType>().sub("games", "coming-soon").pipe(
 *    fields(["created_at"]),
 *    sort("created_at", "desc"),
 *    where("created_at", ">",  now),
 *  )
 * ).execute('http:...').then(response => {
 *   for (const result of response.data) {
 *     if (isNamed(result, 'latest-games')) {
 *       result.result.name // result is of type `{ name: string }`
 *     } else {
 *       result.result.created_at  // result is of type `{ created_at: number }`
 *     }
 *   }
 * });
 * ```
 * @param builder
 * @param name
 */
declare function isNamed<T extends NamedBuilder<any, string>, S extends string>(builder: ResultMultiMono<T>, name: S): builder is ResultMultiMono<Extract<T, NamedBuilder<any, S>>>;

export { AllAutoPath, AllowedValues, AutoPath, Builder, BuilderOperator, BuilderOperatorNarrow, ChosenPaths, CountBuilder, DeepPick, Executor, ExecutorMulti, FallbackIfUnknown, FlatPath, GetOp, NamedBuilder, NamedBuilderOperator, NarrowBuilder, NonEmptyStringList, NumbersOperatos, Options, Path, Pipe, PipeSub, ResultMultiMono, StandardOperators, StringOperators, Stringifiable, WhereFlags, WhereInFlags, and, exclude, fields, isNamed, limit, multi, offset, or, request, search, sort, where, whereIn };
