// Lighter Exact implementation — catches excess top-level properties only.
// Does not recursively check nested objects/arrays. This is intentional:
// DynamoDB entity shapes are flat at the key level.
// Uses DistributiveKeyof so that union-typed Shape (discriminated unions)
// exposes keys from ALL variants, not just the common keys.
type DistributiveKeyof<T> = T extends unknown ? keyof T : never

type Exact<Shape, Candidate> = Candidate & {
    [K in keyof Candidate]: K extends DistributiveKeyof<Shape>
        ? Candidate[K]
        : never
}

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

/**
 * Force an operation like `{ a: 0 } & { b: 1 }` to be computed so that it displays `{ a: 0; b: 1 }`.
 * This version is distributive, meaning it will preserve union types while flattening each member.
 */
type show<T> = T extends unknown
    ? { [K in keyof T]: T[K] } & unknown
    : never

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[] = [],
> = End extends 0
    ? []
    : End extends 1
      ? T extends [infer Head, ...unknown[]]
          ? [Head]
          : []
      : 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 NonEmptyArray<T> = [T, ...T[]]

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

/**
 * Represents a type-level error message. Used to provide helpful feedback in the IDE.
 */
declare const errorMessage: unique symbol
type ErrorMessage<T extends string> = {
    readonly [errorMessage]: T
}

type TransformOverride<Spec extends InputSpecShape, K, Fallback, Matched = Extract<Spec[number], [K, (...args: any[]) => any, ...any[]]>> = [Matched] extends [never] ? Fallback : (Matched extends [any, (x: infer P) => any, ...any[]] ? (x: P) => void : never) extends (x: infer I) => void ? I : Fallback;
type CompositeKeyParamsImpl<Entity, InputSpec extends InputSpecShape, skip extends number = 1> = Entity extends unknown ? show<{
    [K in extractHeadOrPass<SliceFromStart<InputSpec, number extends skip ? 1 : skip>[number]> & keyof Entity]: TransformOverride<InputSpec, K, Entity[K]>;
} & {
    [K in extractHeadOrPass<InputSpec[number]> & keyof Entity]?: TransformOverride<InputSpec, K, Entity[K]>;
}> : 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 ? CompositeKeyStringBuilder<Entity, [
    Deep
] extends [never] ? Spec : number extends Deep ? Spec : SliceFromStart<Spec, Deep>, Separator, boolean extends isPartial ? false : isPartial> : 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 ExtractHelper<Key, Value> = Value extends object ? Value extends {
    tag: infer Tag extends string;
    value: infer Value extends joinable;
} ? [Tag, Value] : Value extends {
    value: infer Value extends joinable;
} ? [never, Value] : never : [Key, Value];
type ExtractPair<Entity, Spec> = Spec extends [
    infer Key extends string,
    (...key: any[]) => infer Value,
    ...unknown[]
] ? ExtractHelper<Uppercase<Key>, Value> : Spec extends keyof Entity & string ? [Uppercase<Spec>, Entity[Spec] & joinable] : never;
type CompositeKeyStringBuilder<Entity, Spec, Separator extends string, KeepIntermediate extends boolean, Acc extends string = '', AllAcc extends string = never> = Spec extends [infer Head, ...infer Tail] ? ExtractPair<Entity, Head> extends [
    infer Key extends joinable,
    infer Value extends joinable
] ? CompositeKeyStringBuilder<Entity, Tail, Separator, KeepIntermediate, Acc extends '' ? [Key] extends [never] ? `${Value}` : `${Key}${Separator}${Value}` : [Key] extends [never] ? `${Acc}${Separator}${Value}` : `${Acc}${Separator}${Key}${Separator}${Value}`, KeepIntermediate extends true ? AllAcc | (Acc extends '' ? never : Acc) : never> : never : AllAcc | Acc;
type DiscriminatedSchemaShape = {
    discriminator: PropertyKey;
    spec: {
        [k in PropertyKey]: unknown;
    };
};
type InputSpecShape = ([PropertyKey, (key: any) => unknown, ...unknown[]] | PropertyKey)[];
type TransformShape = {
    tag?: string;
    value: joinable;
} | joinable;
type ComputeTableKeyType<Entity, Spec, Separator extends string, NullAs extends never | undefined = never> = Spec extends InputSpecShape ? CompositeKeyBuilderImpl<Entity, Spec, Separator, number, false> : Spec extends keyof Entity ? Replace<Entity[Spec], null, undefined> : Spec extends null ? NullAs : never;
type TableEntryImpl<Entity, Schema, Separator extends string = '#'> = Entity extends unknown ? show<{
    readonly [Key in keyof Schema]: Schema[Key] extends DiscriminatedSchemaShape ? ComputeTableKeyType<Entity, ValueOf<Schema[Key]['spec'], ValueOf<Entity, Schema[Key]['discriminator']>>, Separator> : Schema[Key] extends keyof Entity | InputSpecShape | null ? ComputeTableKeyType<Entity, Schema[Key], Separator> : ErrorMessage<'Invalid schema definition'>;
} & Entity> : never;
/**
 * Represents a complete DynamoDB table entry, combining the original entity
 * with its computed internal and global keys.
 *
 * @template Entity The base entity type.
 * @template Schema The schema defining the table keys.
 * @template Separator The string used to join composite key components (default: '#').
 */
type TableEntry<Entity extends Record<string, unknown>, Schema extends Record<string, FullKeySpec<Entity>>, Separator extends string = '#'> = TableEntryImpl<Entity, Schema, Separator>;
type DefaultOf<T> = T extends Record<string, unknown> ? Partial<T> : T;
type InputSpec<E> = {
    [key in keyof E]: (undefined extends E[key] ? [
        key,
        (key: Exclude<E[key], undefined>) => TransformShape,
        DefaultOf<Exclude<E[key], undefined>>
    ] : [key, (key: E[key]) => TransformShape]) | (undefined extends E[key] ? never : null extends E[key] ? never : key);
}[keyof E];
type ValidateInputSpec<T> = {
    [I in keyof T]: T[I] extends readonly [
        unknown,
        (arg: infer P) => any,
        unknown
    ] ? [T[I][0], T[I][1], P] : T[I];
};
type Tuple3 = {
    length: 3;
};
type NeedsValidation<V> = V extends readonly unknown[] ? Extract<V[number], Tuple3> : V extends {
    spec: infer S;
} ? NeedsValidation<S[keyof S]> : never;
type ValidateSchema<Schema> = [NeedsValidation<Schema[keyof Schema]>] extends [
    never
] ? unknown : {
    [K in keyof Schema]: Schema[K] extends {
        discriminator: unknown;
        spec: infer Spec;
    } ? {
        discriminator: Schema[K]['discriminator'];
        spec: {
            [SV in keyof Spec]: ValidateInputSpec<Spec[SV]>;
        };
    } : ValidateInputSpec<Schema[K]>;
};
type extractHeadOrPass<T> = T extends readonly 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>>;
declare class RotoriseError extends Error {
    constructor(message: string);
}
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>> : Spec extends null | undefined ? unknown : ErrorMessage<'Invalid Spec: Expected string, InputSpecShape, null or undefined'>;
type SpecConfig<Spec> = Spec extends string ? never : SpecConfigShape;
type SpecConfigShape = {
    depth?: number;
    allowPartial?: boolean;
    enforceBoundary?: boolean;
};
type ExtractVariant<Entity, K extends PropertyKey, V extends PropertyKey> = [
    Entity
] extends [never] ? never : Extract<Entity, {
    [k in K]: V;
}>;
type TagVariant<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, VariantSpec = Spec['spec'][V & keyof Spec['spec']]> = TagVariant<VariantSpec extends null | undefined ? unknown : ProcessSpecType<ExtractVariant<Entity, K, V>, VariantSpec, Config>, K, V>;
type OptimizedAttributes<Entity, Spec, Config extends SpecConfigShape> = show<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 | undefined ? NullAs : ErrorMessage<'Invalid Spec'>;
type OptimizedBuiltKey<Entity, Spec, Separator extends string, Config extends SpecConfigShape, Attributes> = Entity extends unknown ? show<Spec extends DiscriminatedSchemaShape ? ProcessKey<Entity, ValueOf<Spec['spec'], ValueOf<Entity, Spec['discriminator']>>, Separator, undefined, Config, Attributes> : ProcessKey<Entity, Spec, Separator, undefined, Config, Attributes>> : never;
type TableEntryDefinition<Entity, Schema, Separator extends string> = {
    /**
     * Converts a raw entity into a complete table entry with all keys computed.
     * Use this when preparing items for insertion into DynamoDB.
     */
    toEntry: <const ExactEntity>(item: Exact<Entity, ExactEntity>) => TableEntryImpl<ExactEntity, Schema, Separator>;
    /**
     * Extracts the raw entity from a table entry by removing all computed keys.
     * Use this when processing items retrieved from DynamoDB.
     */
    fromEntry: <const Entry extends TableEntryImpl<Entity, Schema, Separator>>(entry: Entry) => DistributiveOmit<Entry, keyof Schema>;
    /**
     * Generates a specific key for the given entity attributes.
     * Supports partial keys and depth limiting for query operations.
     *
     * @param key The name of the key to generate (e.g., 'PK', 'GSIPK').
     * @param attributes the object containing the values needed to build the key.
     * @param config Optional configuration for partial keys or depth limiting.
     */
    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;
        enforceBoundary?: boolean;
    } : Config>(key: Key, attributes: Attributes, config?: Config) => OptimizedBuiltKey<Attributes, Spec, Separator, Config_, Attributes>;
    /**
     * A zero-runtime inference helper. Use this with `typeof` to get the
     * total type of a table entry.
     */
    infer: TableEntryImpl<Entity, Schema, Separator>;
    /**
     * Creates a proxy to generate property paths as strings.
     * Useful for building UpdateExpressions or ProjectionExpressions.
     *
     * @example
     * table.path().data.nested.property.toString() // returns "data.nested.property"
     */
    path: () => TableEntryImpl<Entity, Schema, Separator>;
};
/**
 * Entry point for defining a DynamoDB table schema with Rotorise.
 *
 * @template Entity The base entity type that this table represents.
 * @returns A builder function that accepts the schema and an optional separator.
 *
 * Note: the double-call `<Entity>()(schema)` is required for partial type parameter inference.
 *
 * @example
 * const userTable = tableEntry<User>()({
 *   PK: ["orgId", "id"],
 *   SK: "role"
 * })
 */
declare const tableEntry: <const Entity extends Record<string, unknown>>() => <const Schema extends Record<string, FullKeySpec<Entity>>, Separator extends string = "#">(schema: Schema & ValidateSchema<Schema>, ...[separator]: [Separator] extends [
    ''
] ? [ErrorMessage<'Separator must not be an empty string'>] : [separator?: Separator]) => TableEntryDefinition<Entity, Schema, Separator>;

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