import { IsArrayStrict, Prettify } from "../helpers";
import { Call, Fn, PartialApply, unset, _ } from "../core/Core";
import { Std } from "../std/Std";
import { Strings } from "../strings/Strings";
import * as Impl from "./impl/objects";
export declare namespace Objects {
    /**
     * Create an object from a union of `[key, value]` entries.
     * @param entries - union of entries to convert to an object
     * @returns an object
     * @example
     * ```ts
     * type T0 = Call<Objects.FromEntries, ["a", 1] | ["b", true]>; // { a: 1; b: true }
     * ```
     */
    export interface FromEntries extends Fn {
        return: Impl.FromEntries<Extract<this["arg0"], [PropertyKey, any]>>;
    }
    /**
     * Turn an object into a union of entries
     * @param obj - The object to transform to entries
     * @returns a union of `[key, value]` entry tuples.
     * @example
     * ```ts
     * type T0 = Call<Objects.Entries, { a: 1; b: true }>; // ["a", 1] | ["b", true]
     * ```
     */
    export interface Entries extends Fn {
        return: Impl.Entries<this["arg0"]>;
    }
    type MapValuesImpl<T, fn extends Fn> = {
        [K in keyof T]: Call<fn, T[K], K>;
    };
    /**
     * Map over values in an object type.
     * @param fn - The function to run on every object value
     * @param obj - The object to transform
     * @returns a new object with updated values
     * @example
     * ```ts
     * type T0 = Call<
     *   Objects.MapValues<Strings.ToNumber>,
     *   { a: "1"; b: "2" }
     * >; // { a: 1, b: 2 }
     * ```
     */
    export interface MapValues<fn extends Fn> extends Fn {
        return: MapValuesImpl<this["arg0"], fn>;
    }
    type MapKeysImpl<T, fn extends Fn> = {
        [K in keyof T as Extract<Call<fn, K>, PropertyKey>]: T[K];
    };
    /**
     * Map over keys in an object type.
     * @param fn - The function to run on every object key
     * @param obj - The object to transform
     * @returns a new object with updated keys
     * @example
     * ```ts
     * type T0 = Call<
     *   Objects.MapKeys<Strings.Uppercase>,
     *   { a: 1; b: 2 }
     * >; // { A: 1, B: 2 }
     * ```
     */
    export interface MapKeys<fn extends Fn> extends Fn {
        return: MapKeysImpl<this["arg0"], fn>;
    }
    /**
     * Turn object keys into kebab-case
     * @param obj - The object to transform
     * @returns a new object with updated keys
     * @example
     * ```ts
     * type T0 = Call<
     *   Objects.KebabCase,
     *   { userName: "Bob" }
     * >; // { "user-name": "Bob" }
     * ```
     */
    export interface KebabCase extends Fn {
        return: Call<MapKeys<Strings.KebabCase>, this["arg0"]>;
    }
    /**
     * Turn object keys into snake_case
     * @param obj - The object to transform
     * @returns a new object with updated keys
     * @example
     * ```ts
     * type T0 = Call<
     *   Objects.SnakeCase,
     *   { userName: "Bob" }
     * >; // { user_name: "Bob" }
     * ```
     */
    export interface SnakeCase extends Fn {
        return: Call<MapKeys<Strings.SnakeCase>, this["arg0"]>;
    }
    /**
     * Turn object keys into camelCase
     * @param obj - The object to transform
     * @returns a new object with updated keys
     * @example
     * ```ts
     * type T0 = Call<
     *   Objects.CamelCase,
     *   { user_name: "Bob" }
     * >; // { userName: "Bob" }
     * ```
     */
    export interface CamelCase extends Fn {
        return: Call<MapKeys<Strings.CamelCase>, this["arg0"]>;
    }
    type MapKeysDeepImpl<T, fn extends Fn> = IsArrayStrict<T> extends true ? MapKeysDeepImpl<Extract<T, readonly any[]>[number], fn>[] : T extends object ? {
        [K in keyof T as Extract<Call<fn, K>, PropertyKey>]: Prettify<MapKeysDeepImpl<T[K], fn>>;
    } : T;
    /**
     * Recursively transform all keys in a structure of object types
     * @param fn - The function to apply to every key
     * @param obj - The object to transform
     * @returns a new object with updated keys
     * @example
     * ```ts
     * type T0 = Call<
     *   Objects.MapKeysDeep<Strings.Prepend<'_'>>,
     *   { a: { b: { c: string } } }
     * >; // { _a: { _b: { _c: string } } }
     * ```
     */
    export interface MapKeysDeep<fn extends Fn> extends Fn {
        return: MapKeysDeepImpl<this["arg0"], fn>;
    }
    /**
     * Recursively transform all keys of a deeply nested object
     * to kebab-case.
     * @param obj - The object to transform
     * @returns a new object with updated keys
     * @example
     * ```ts
     * type T0 = Call<
     *   Objects.KebabCaseDeep,
     *   { currentUser: { firstName: string } }
     * >; // { "current-user": { "first-name": string } }
     * ```
     */
    export interface KebabCaseDeep extends Fn {
        return: Call<MapKeysDeep<Strings.KebabCase>, this["arg0"]>;
    }
    /**
     * Recursively transform all keys of a deeply nested object
     * to snake_case.
     * @param obj - The object to transform
     * @returns a new object with updated keys
     * @example
     * ```ts
     * type T0 = Call<
     *   Objects.SnakeCaseDeep,
     *   { currentUser: { firstName: string } }
     * >; // { current_user: { first_name: string } }
     * ```
     */
    export interface SnakeCaseDeep extends Fn {
        return: Call<MapKeysDeep<Strings.SnakeCase>, this["arg0"]>;
    }
    /**
     * Recursively transform all keys of a deeply nested object
     * to camelCase.
     * @param obj - The object to transform
     * @returns a new object with updated keys
     * @example
     * ```ts
     * type T0 = Call<
     *   Objects.CamelCaseDeep,
     *   { current_user: { first_name: string } }
     * >; // { currentUser: { firstName: string } }
     * ```
     */
    export interface CamelCaseDeep extends Fn {
        return: Call<MapKeysDeep<Strings.CamelCase>, this["arg0"]>;
    }
    /**
     * Only keep keys of an object if they are
     * assignable to some type.
     * @param key - The type remaining keys should be assignable to
     * @param obj - The object to filter
     * @returns a filtered object
     * @example
     * ```ts
     * type T0 = Call<
     *   Objects.Pick<"a" | "b">,
     *   { a: 1, b: 1, c: 1 }
     * >; // { a: 1, b: 1 }
     * ```
     */
    export type Pick<key = unset, obj = unset> = PartialApply<PickFn, [key, obj]>;
    interface PickFn extends Fn {
        return: PickImpl<this["arg1"], this["arg0"]>;
    }
    type PickImpl<obj, keys> = {
        [key in Extract<keyof obj, keys>]: obj[key];
    };
    /**
     * Only keep keys of an object if they aren't
     * assignable to some type.
     * @param key - The type remaining keys should _not_ be assignable to
     * @param obj - The object to filter
     * @returns a filtered object
     * @example
     * ```ts
     * type T0 = Call<
     *   Objects.Pick<"a" | "b">,
     *   { a: 1, b: 1, c: 1 }
     * >; // { c: 1 }
     * ```
     */
    export type Omit<key = unset, obj = unset> = PartialApply<OmitFn, [key, obj]>;
    interface OmitFn extends Fn {
        return: OmitImpl<this["arg1"], this["arg0"]>;
    }
    type OmitImpl<obj, keys> = {
        [key in Exclude<keyof obj, keys>]: obj[key];
    };
    /**
     * Only keep keys of an object if the predicate function
     * returns true
     * @param fn - The predicate function, taking (value, key) as arguments
     * @param obj - The object to filter
     * @returns a filtered object
     * @example
     * ```ts
     * type T0 = Call<
     *   Objects.PickBy<Strings.StartsWith<"a">>,
     *   { a: "ab", b: "ac", c: "bc" }
     * >; // { a: "ab", b: "ac" }
     * ```
     */
    export interface PickBy<fn extends Fn> extends Fn {
        return: PickByImpl<this["arg0"], fn>;
    }
    type PickByImpl<T, fn extends Fn> = Impl.FromEntries<PickEntriesImpl<Impl.Entries<T>, fn>>;
    type PickEntriesImpl<entries extends [PropertyKey, any], fn extends Fn> = entries extends any ? Call<fn, entries[1], entries[0]> extends true ? entries : never : never;
    /**
     * Only keep keys of an object if the predicate function
     * returns false
     * @param fn - The predicate function, taking (value, key) as arguments
     * @param obj - The object to filter
     * @returns a filtered object
     * @example
     * ```ts
     * type T0 = Call<
     *   Objects.PickBy<Strings.StartsWith<"a">>,
     *   { a: "ab", b: "ac", c: "bc" }
     * >; // { c: "bc" }
     * ```
     */
    export interface OmitBy<fn extends Fn> extends Fn {
        return: OmitByImpl<this["arg0"], fn>;
    }
    type OmitByImpl<T, fn extends Fn> = Impl.FromEntries<OmitEntriesImpl<Impl.Entries<T>, fn>>;
    type OmitEntriesImpl<entries extends [PropertyKey, any], fn extends Fn> = entries extends any ? Call<fn, entries[1], entries[0]> extends true ? never : entries : never;
    /**
     * Merge several objects together
     * @param {...objects} - Objects to merge
     * @returns a merged object
     * @example
     * ```ts
     * type T0 = Call<Objects.Assign<{ a: string }>, { b: number }>; // { a: string, b: number }
     * type T1 = Call<Objects.Assign<{ a: string }, { b: number }>>; // { a: string, b: number }
     * type T2 = Call<Objects.Assign<{ a: 1 }, { b: 1 }, { c: 1 }>>; // { a: 1, b: 1, c: 1 }
     * ```
     */
    export type Assign<arg1 = unset, arg2 = unset, arg3 = unset, arg4 = unset, arg5 = unset> = PartialApply<AssignFn, [arg1, arg2, arg3, arg4, arg5]>;
    interface AssignFn extends Fn {
        return: Impl.Assign<this["args"]>;
    }
    /**
     * Make all properties of an object readonly
     * @description This function is used to make properties of an object readonly
     * @param value - The object to make properties readonly
     * @returns The object with its properties made readonly
     * @example
     * ```ts
     * type T0 = Call<Objects.Readonly, { a: 1; b: true }>; // { readonly a:1; readonly b: true}
     * type T1 = Call<Objects.Readonly<{ a: 1; b: true }>>; // { readonly a:1; readonly b: true}
     * ```
     */
    export type Readonly<value = unset> = PartialApply<ReadonlyFn, [value]>;
    interface ReadonlyFn extends Fn {
        return: this["args"] extends [infer value] ? Std._Readonly<value> : never;
    }
    /**
     * Make all properties of an object required
     * @description This function is used to make properties of an object required
     * @param value - The object to make properties required
     * @returns The object with its properties made required
     * @example
     * ```ts
     * type T0 = Call<Objects.Required, { a?: 1; b?: true }>; // { a:1; b: true}
     * type T1 = Call<Objects.Required<{ a?: 1; b?: true }>>; // { a:1; b: true}
     * ```
     */
    export type Required<value = unset> = PartialApply<RequiredFn, [value]>;
    interface RequiredFn extends Fn {
        return: this["args"] extends [infer value] ? Std._Required<value> : never;
    }
    /**
     * Make all properties of an object optional
     * @description This function is used to make properties of an object optional
     * @param value - The object to make properties optional
     * @returns The object with its properties made optional
     * @example
     * ```ts
     * type T0 = Call<Objects.Partial, { a: 1; b: true }>; // { a?:1; b?: true}
     * type T1 = Call<Objects.Partial<{ a: 1; b: true }>>; // { a?:1; b?: true}
     * ```
     */
    export type Partial<value = unset> = PartialApply<PartialFn, [value]>;
    interface PartialFn extends Fn {
        return: this["args"] extends [infer value] ? Std._Partial<value> : never;
    }
    /**
     * Make all properties of an object mutable
     * @param value - The object to make properties mutable
     * @returns The object with its properties made mutable
     * @example
     * ```ts
     * type T0 = Call<Objects.Mutable, { readonly a: 1; readonly b: true }>; // { a:1; b: true }
     * ```
     */
    export type Mutable<obj = unset> = PartialApply<MutableFn, [obj]>;
    interface MutableFn extends Fn {
        return: this["args"] extends [infer obj, ...any] ? {
            -readonly [key in keyof obj]: obj[key];
        } : never;
    }
    /**
     * Makes all levels of an object optional
     * @description This function is used to make all levels of an object optional
     * @param obj - The object to make levels optional
     * @returns The object with its levels made optional
     *
     * @example
     * ```ts
     * type T0 = Call<Objects.PartialDeep, { a: 1; b: true }>;
     * //   ^? { a?:1; b?: true}
     * type T1 = Call<Objects.PartialDeep, { a: 1; b: { c: true } }>;
     * //   ^? { a?:1; b?: { c?: true } }
     * type T2 = Call<Objects.PartialDeep, { a: 1; b: { c: true, d: { e: false } } }>;
     * //   ^? { a?:1; b?: { c?: true, d?: { e?: false } } }
     */
    export type PartialDeep<obj = unset> = PartialApply<PartialDeepFn, [obj]>;
    interface PartialDeepFn extends Fn {
        return: this["args"] extends [infer obj] ? Impl.TransformObjectDeep<PartialFn, obj> : never;
    }
    /**
     * Makes all levels of an object required
     * @description This function is used to make all levels of an object required
     * @param obj - The object to make levels required
     * @returns The object with its levels made required
     *
     * @example
     * ```ts
     * type T0 = Call<Objects.RequiredDeep, { a?:1; b?: true }>;
     * //   ^? { a: 1; b: true }
     * type T1 = Call<Objects.RequiredDeep, { a?:1; b?: { c?: true } }>;
     * //   ^? { a: 1; b: { c: true } }
     * type T2 = Call<Objects.RequiredDeep, { a?:1; b?: { c?: true, d?: { e?: false } } }>;
     * //   ^? { a: 1; b: { c: true, d: { e: false } } }
     */
    export type RequiredDeep<obj = unset> = PartialApply<RequiredDeepFn, [obj]>;
    interface RequiredDeepFn extends Fn {
        return: this["args"] extends [infer obj] ? Impl.TransformObjectDeep<RequiredFn, obj> : never;
    }
    /**
     * Makes all levels of an object readonly
     * @description This function is used to make all levels of an object readonly
     * @param obj - The object to make levels readonly
     * @returns The object with its levels made readonly
     *
     * @example
     * ```ts
     * type T0 = Call<Objects.ReadonlyDeep, { a:1; b: true }>;
     * //   ^? { readonly a: 1; readonly b: true }
     * type T1 = Call<Objects.ReadonlyDeep, { a:1; b: { c: true } }>;
     * //   ^? { readonly a: 1; readonly b: { readonly c: true } }
     * type T2 = Call<Objects.ReadonlyDeep, { a:1; b: { c: true, d: { e: false } } }>;
     * //   ^? { readonly a: 1; readonly b: { readonly c: true, d: { readonly e: false } } }
     */
    export type ReadonlyDeep<obj = unset> = PartialApply<ReadonlyDeepFn, [obj]>;
    interface ReadonlyDeepFn extends Fn {
        return: this["args"] extends [infer obj] ? Impl.TransformObjectDeep<ReadonlyFn, obj> : never;
    }
    /**
     * Makes all levels of an object mutable
     * @description This function is used to make all levels of an object mutable
     * @param obj - The object to make levels mutable
     * @returns The object with its levels made mutable
     *
     * @example
     * ```ts
     * type T0 = Call<Objects.MutableDeep, { readonly a: 1; readonly b: true }>;
     * //   ^? { a:1; b: true }
     * type T1 = Call<Objects.MutableDeep, { readonly a: 1; readonly b: { readonly c: true } }>;
     * //   ^? { a:1; b: { c: true } }
     * type T2 = Call<Objects.MutableDeep, { readonly a: 1; readonly b: { readonly c: true, d: { readonly e: false } } }>;
     * //   ^? { a:1; b: { c: true, d: { e: false } } }
     */
    export type MutableDeep<obj = unset> = PartialApply<MutableDeepFn, [obj]>;
    interface MutableDeepFn extends Fn {
        return: this["args"] extends [infer obj] ? Impl.TransformObjectDeep<MutableFn, obj> : never;
    }
    /**
     * Get a value within an object or a tuple type.
     * @description This function takes an object, a path to one of its properties,
     * and returns the property under that path.
     * @param path - the path to the property to get.
     * @param obj - the object to read from.
     * @returns The value under that path
     *
     * @example
     * ```ts
     * type T0 = Call<O.Get<'a'>, { a: 1, b: 1 }>; // 1
     * type T1 = Call<O.Get<'a[0]'>, { a: [1, 2, 3] }>; // 1
     * type T2 = Call<O.Get<'a.b'>, { a: { b: 1 }, c: '' }>; // 1
     * ```
     */
    export type Get<path extends string | number | _ | unset = unset, obj = unset> = PartialApply<GetFn, [path, obj]>;
    export interface GetFn extends Fn {
        return: this["args"] extends [
            infer path extends string | number,
            infer obj,
            ...any
        ] ? Impl.GetFromPath<obj, path> : never;
    }
    /**
     * Updates an object or a tuple type.
     * @description This function takes an object, a path to one of its properties,
     * a new value or a function to apply to this property, and returns a new version
     * of this object with this property updated.
     * @param path - the path to the property to update
     * @param valueOrFn - a value to set, or a function to apply on the target property
     * @param obj - the object to update.
     * @returns The updated object.
     *
     * @example
     * ```ts
     * type T0 = Call<O.Update<'a', Numbers.Add<1>>, { a: 1, b: 1 }>; // { a: 2, b: 1 }
     * type T1 = Call<O.Update<'a[0]', 4>, { a: [1, 2, 3] }>; // { a: [4, 2, 3] }
     * type T2 = Call<O.Update<'a.b', Numbers.Add<1>>, { a: { b: 1 }, c: '' }>; // { a: { b: 2 }, c: '' }
     * type T3 = Call<O.Update<'a.b', "Hello">, { a: { b: 1 } }>; // { a: { b: "Hello" } }
     * ```
     */
    export type Update<path extends string | number | _ | unset = unset, fnOrValue = unset, obj = unset> = PartialApply<UpdateFn, [path, fnOrValue, obj]>;
    export interface UpdateFn extends Fn {
        return: this["args"] extends [
            infer path extends string | number,
            infer fnOrValue,
            infer obj,
            ...any
        ] ? Impl.Update<obj, path, fnOrValue> : never;
    }
    /**
     * Create an object from parameters
     * @description This function is used to make an object from parameters
     * And allows to place the parameters in any object property
     * @param args - The parameters to make the object from
     * @returns The object made from the parameters
     *
     * @example
     * ```ts
     * type T0 = Apply<O.Create<{ a: arg0, b: arg1 }>, [1, 2]>; // { a: 1, b: 2 }
     * ```
     */
    interface CreateFn extends Fn {
        return: this["args"] extends [infer pattern, ...infer args] ? Impl.Create<pattern, args> : never;
    }
    export type Create<pattern = unset, arg0 = unset, arg1 = unset, arg2 = unset, arg3 = unset> = PartialApply<CreateFn, [pattern, arg0, arg1, arg2, arg3]>;
    interface RecordFn extends Fn {
        return: this["args"] extends [infer union extends string, infer value] ? Std._Record<union, value> : never;
    }
    /**
     * Create a record from a union of strings and a value type
     * @description This function is used to create a record from a union of strings
     * @param union - The union of strings to create the record from
     * @param value - The value to assign to each property
     * @returns The record created from the union of strings
     *
     * @example
     * ```ts
     * type T0 = Call<O.Record<'a' | 'b' | 'c'>, 1>; // { a: 1, b: 1, c: 1 }
     * ```
     */
    export type Record<union extends string | _ | unset = unset, value = unset> = PartialApply<RecordFn, [union, value]>;
    /**
     * Smarter version of keyof that also works with tuples
     * @params args[0] - The type to extract keys from
     * @returns An union of all the types's keys
     * @example
     * ```ts
     * type T0 = Call<O.Keys, ['a', 'b', 'c']>; // 0 | 1 | 2
     * ```
     */
    export interface Keys extends Fn {
        return: Impl.Keys<this["arg0"]>;
    }
    /**
     * Smarter version of Values that also works with tuples
     * @params args[0] - The type to extract values from
     * @returns An union of all the types's values
     * @example
     * ```ts
     * type T0 = Call<O.Values, ['a', 'b', 'c']>; // 'a' | 'b' | 'c'
     * ```
     */
    export interface Values extends Fn {
        return: Impl.Values<this["arg0"]>;
    }
    /**
     * Create a union of all deep paths the object has
     * @description This function is used to create a union from an object with keys
     * @param obj - The object from which the union will be generated
     * @returns An union with all the possible deep paths
     *
     * @example
     * ```ts
     * type T0 = Call<O.AllPaths, { a: { b: number } }>; // 'a' | 'a.b'
     * ```
     */
    export interface AllPaths extends Fn {
        return: Impl.AllPaths<this["arg0"]>;
    }
    export {};
}
