type KeysOfUnion<ObjectType> = ObjectType extends unknown
    ? keyof ObjectType
    : never
type IsEqual<T, U> = (<G>() => G extends T ? 1 : 2) extends <
    G,
>() => G extends U ? 1 : 2
    ? true
    : false

type ArrayElement<T> = T extends readonly unknown[] ? T[0] : never

type ExactObject<ParameterType, InputType> = {
    [Key in keyof ParameterType]: Exact<
        ParameterType[Key],
        Key extends keyof InputType ? InputType[Key] : never
    >
} & Record<Exclude<keyof InputType, KeysOfUnion<ParameterType>>, never>

type Exact<ParameterType, InputType> = IsEqual<
    ParameterType,
    InputType
> extends true
    ? ParameterType
    : // Convert union of array to array of union: A[] & B[] => (A & B)[]
      ParameterType extends unknown[]
      ? Array<Exact<ArrayElement<ParameterType>, ArrayElement<InputType>>>
      : // In TypeScript, Array is a subtype of ReadonlyArray, so always test Array before ReadonlyArray.
        ParameterType extends readonly unknown[]
        ? ReadonlyArray<
              Exact<ArrayElement<ParameterType>, ArrayElement<InputType>>
          >
        : ParameterType extends object
          ? ExactObject<ParameterType, InputType>
          : ParameterType

type ValueOf<
    ObjectType,
    ValueType extends keyof ObjectType = keyof ObjectType,
> = ObjectType[ValueType]

type evaluate<T> = { [K in keyof T]: T[K] } & unknown

type DistributivePick<T, K> = T extends unknown
    ? K extends keyof T
        ? Pick<T, K>
        : never
    : never

type DistributiveOmit<T, K extends keyof T> = T extends unknown
    ? Omit<T, K>
    : never

type SliceFromStart<
    T,
    End extends number,
    Acc extends unknown[] = [],
> = T extends unknown[]
    ? Acc['length'] extends End
        ? Acc
        : T extends [infer Head, ...infer Tail]
          ? SliceFromStart<Tail, End, [...Acc, Head]>
          : Acc
    : never

type MergeIntersectionObject<T, Keys = keyof T> = {
    [K in Keys]: T[K]
}
// type t = MergeIntersectionObject<
//     { a: 'a1'; b: 1n; extra: 'extra' } | { a: 'a2'; b: 2 }
// >

type NonEmptyArray<T> = [T, ...T[]]

type Replace<T, U, V> = T extends U ? V : T

type CompositeKeyParamsImpl<Entity, InputSpec extends InputSpecShape, skip extends number = 1> = Entity extends unknown ? evaluate<Pick<Entity, extractHeadOrPass<SliceFromStart<InputSpec, number extends skip ? 1 : skip>[number]> & keyof Entity> & Partial<Pick<Entity, extractHeadOrPass<InputSpec[number]> & keyof Entity>>> : never;
type CompositeKeyParams<Entity extends Record<string, unknown>, FullSpec extends InputSpec<MergeIntersectionObject<Entity>>[], skip extends number = 1> = CompositeKeyParamsImpl<Entity, FullSpec, skip>;
type CompositeKeyBuilderImpl<Entity, Spec, Separator extends string = '#', Deep extends number = number, isPartial extends boolean = false> = Entity extends unknown ? Join<CompositeKeyRec<Entity, number extends Deep ? Spec : SliceFromStart<Spec, Deep>>, Separator, (boolean extends isPartial ? false : isPartial) extends false ? false : true> : never;
type CompositeKeyBuilder<Entity extends Record<string, unknown>, Spec extends InputSpec<MergeIntersectionObject<Entity>>[], Separator extends string = '#', Deep extends number = number, isPartial extends boolean = false> = CompositeKeyBuilderImpl<Entity, Spec, Separator, Deep, isPartial>;
type joinable = string | number | bigint | boolean | null | undefined;
type joinablePair = [joinable, joinable];
type Join<Pairs, Separator extends string, KeepIntermediate extends boolean = false, Acc extends string = '', AllAcc extends string = never> = Pairs extends [infer Head extends joinablePair, ...infer Tail] ? Join<Tail, Separator, KeepIntermediate, Acc extends '' ? `${Head[0]}${Separator}${Head[1]}` : `${Acc}${Separator}${Head[0]}${Separator}${Head[1]}`, KeepIntermediate extends true ? AllAcc | (Acc extends '' ? never : Acc) : never> : AllAcc | Acc;
type ExtractPair<Entity, Spec> = Spec extends [
    infer Key extends string,
    (...key: any[]) => infer Value extends joinable
] ? [Uppercase<Key>, Value] : Spec extends keyof Entity & string ? [Uppercase<Spec>, Entity[Spec] & joinable] : never;
type CompositeKeyRec<Entity, Spec, Acc extends joinablePair[] = [], KeysCache extends string = keyof Entity & string> = Spec extends [infer Head, ...infer Tail] ? CompositeKeyRec<Entity, Tail, [
    ...Acc,
    ExtractPair<Entity, Head>
], KeysCache> : Acc;
type DiscriminatedSchemaShape = {
    discriminator: PropertyKey;
    spec: {
        [k in PropertyKey]: unknown;
    };
};
type InputSpecShape = ([PropertyKey, (key: any) => unknown] | PropertyKey)[];
type TableEntryImpl<Entity, Schema, Separator extends string = '#'> = Entity extends unknown ? {
    [Key in keyof Schema]: Schema[Key] extends DiscriminatedSchemaShape ? ValueOf<{
        [K in Schema[Key]['discriminator']]: {
            [V in keyof Schema[Key]['spec']]: ProcessKey<Entity, Schema[Key]['spec'][V], Separator>;
        }[Entity[K & keyof Entity] & keyof Schema[Key]['spec']];
    }> : ProcessKey<Entity, Schema[Key], Separator>;
} & Entity : never;
type TableEntry<Entity extends Record<string, unknown>, Schema extends Record<string, FullKeySpec<Entity>>, Separator extends string = '#'> = TableEntryImpl<Entity, Schema, Separator>;
type InputSpec<E> = {
    [key in keyof E]: [key, (key: E[key]) => unknown] | (undefined extends E[key] ? never : null extends E[key] ? never : key);
}[keyof E];
type extractHeadOrPass<T> = T extends unknown[] ? T[0] : T;
type FullKeySpecSimple<Entity> = NonEmptyArray<InputSpec<MergeIntersectionObject<Entity>>> | (keyof Entity & string) | null;
type DiscriminatedSchema<Entity, E> = {
    [key in keyof E]: E[key] extends PropertyKey ? {
        discriminator: key;
        spec: {
            [val in E[key]]: FullKeySpecSimple<Extract<Entity, {
                [k in key]: val;
            }>>;
        };
    } : never;
}[keyof E];
type FullKeySpec<Entity> = FullKeySpecSimple<Entity> | DiscriminatedSchema<Entity, MergeIntersectionObject<Entity>>;
type ProcessSpecType<Entity, Spec, Config extends SpecConfigShape> = Spec extends string ? DistributivePick<Entity, Spec> : Spec extends InputSpecShape ? CompositeKeyParamsImpl<Entity, Spec, Config['allowPartial'] extends true ? 1 : Extract<Config['depth'], number>> : never;
type SpecConfig<Spec> = Spec extends string ? never : SpecConfigShape;
type SpecConfigShape = {
    depth?: number;
    allowPartial?: boolean;
};
type VariantType<Entity, K extends PropertyKey, V extends PropertyKey> = [
    Entity
] extends [never] ? {
    [k in K]: V;
} : Entity & {
    [k in K]: V;
};
type ProcessVariant<Entity, K extends PropertyKey, V extends PropertyKey, Spec extends DiscriminatedSchemaShape, Config extends SpecConfigShape> = VariantType<ProcessSpecType<VariantType<Entity, K, V>, Spec['spec'][V & keyof Spec['spec']], Config>, K, V>;
type OptimizedAttributes<Entity, Spec, Config extends SpecConfigShape> = Spec extends DiscriminatedSchemaShape ? {
    [K in Spec['discriminator']]: {
        [V in keyof Spec['spec']]: ProcessVariant<Entity, K, V, Spec, Config>;
    }[keyof Spec['spec']];
}[Spec['discriminator']] : ProcessSpecType<Entity, Spec, Config>;
type ProcessKey<Entity, Spec, Separator extends string, NullAs extends never | undefined = never, Config extends SpecConfigShape = SpecConfigShape, Attributes = Pick<Entity, Spec & keyof Entity>> = [Entity] extends [never] ? never : Spec extends keyof Entity ? Replace<ValueOf<Attributes>, null, undefined> : Spec extends InputSpecShape ? CompositeKeyBuilderImpl<Entity, Spec, Separator, Exclude<Config['depth'], undefined>, Exclude<Config['allowPartial'], undefined>> : Spec extends null ? NullAs : never;
type OptimizedBuildedKey<NarrowEntity, Spec, Separator extends string, Config extends SpecConfigShape, Attributes> = Spec extends DiscriminatedSchemaShape ? {
    [K in Spec['discriminator']]: {
        [V in keyof Spec['spec']]: ProcessKey<NarrowEntity extends {
            [k in K]: V;
        } ? NarrowEntity : never, Spec['spec'][V], Separator, undefined, Config, Attributes>;
    }[keyof Spec['spec']];
}[Spec['discriminator']] : ProcessKey<NarrowEntity, Spec, Separator, undefined, Config, Attributes>;
type TableEntryDefinition<Entity, Schema, Separator extends string> = {
    toEntry: <const ExactEntity extends Exact<Entity, ExactEntity>>(item: ExactEntity) => ExactEntity extends infer E extends Entity ? TableEntryImpl<E, Schema, Separator> : never;
    fromEntry: <const Entry extends TableEntryImpl<Entity, Schema, Separator>>(entry: Entry) => DistributiveOmit<Entry, keyof Schema>;
    key: <const Key extends keyof Schema, const Config extends SpecConfig<Spec>, const Attributes extends OptimizedAttributes<Entity, Spec, Config_>, Spec = Schema[Key], Config_ extends SpecConfigShape = [SpecConfigShape] extends [Config] ? {
        depth?: undefined;
        allowPartial?: undefined;
    } : Config>(key: Key, attributes: Attributes, config?: Config) => OptimizedBuildedKey<Entity & Attributes, Spec, Separator, Config_, Attributes>;
    infer: TableEntryImpl<Entity, Schema, Separator>;
    path: () => TableEntryImpl<Entity, Schema, Separator>;
};
declare const tableEntry: <const Entity extends Record<string, unknown>>() => <const Schema extends Record<string, FullKeySpec<Entity>>, Separator extends string = "#">(schema: Schema, separator?: Separator) => TableEntryDefinition<Entity, Schema, Separator>;

export { type CompositeKeyBuilder, type CompositeKeyParams, type CompositeKeyParamsImpl, type TableEntry, tableEntry };
