import type { OpenApiDocument } from './openapi-document.js';
import type { RestApi } from './rest-api.js';
/**
 * Converts an OpenAPI `{param}` path to FuryStack `:param` format at the type level.
 */
export type ConvertOpenApiPath<P extends string> = P extends `${infer Before}{${infer Param}}${infer After}` ? `${Before}:${Param}${ConvertOpenApiPath<After>}` : P;
type ResolveRef<Doc extends OpenApiDocument, Ref extends string> = Ref extends `#/components/schemas/${infer Name}` ? Doc['components'] extends {
    schemas: infer S;
} ? Name extends keyof S ? S[Name] : unknown : unknown : Ref extends `#/components/parameters/${infer Name}` ? Doc['components'] extends {
    parameters: infer S;
} ? Name extends keyof S ? S[Name] : unknown : unknown : Ref extends `#/components/responses/${infer Name}` ? Doc['components'] extends {
    responses: infer S;
} ? Name extends keyof S ? S[Name] : unknown : unknown : Ref extends `#/components/requestBodies/${infer Name}` ? Doc['components'] extends {
    requestBodies: infer S;
} ? Name extends keyof S ? S[Name] : unknown : unknown : unknown;
type ResolveSchemaOrRef<Doc extends OpenApiDocument, S> = S extends {
    $ref: infer Ref extends string;
} ? ResolveRef<Doc, Ref> : S;
type UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
/**
 * Maps a JSON Schema type keyword to its TypeScript equivalent.
 * Supports primitives, arrays, objects, enum, const, oneOf/anyOf/allOf, nullable, and $ref.
 *
 * The `Doc` parameter is threaded through for `$ref` resolution within schemas.
 */
export type JsonSchemaToType<S, Doc extends OpenApiDocument = OpenApiDocument> = S extends {
    $ref: infer Ref extends string;
} ? JsonSchemaToType<ResolveRef<Doc, Ref>, Doc> : S extends {
    const: infer C;
} ? C : S extends {
    type: 'string';
    enum: ReadonlyArray<infer E>;
} ? E : S extends {
    type: 'string';
} ? string : S extends {
    type: 'number' | 'integer';
} ? number : S extends {
    type: 'boolean';
} ? boolean : S extends {
    type: 'null';
} ? null : S extends {
    type: readonly [infer T, 'null'];
} ? JsonSchemaToType<{
    type: T;
}, Doc> | null : S extends {
    type: readonly ['null', infer T];
} ? JsonSchemaToType<{
    type: T;
}, Doc> | null : S extends {
    type: 'array';
    items: infer Items;
} ? Array<JsonSchemaToType<Items, Doc>> : S extends {
    type: 'object';
    properties: infer Props extends Record<string, unknown>;
} ? S extends {
    required: ReadonlyArray<infer R extends string>;
} ? {
    [K in keyof Props & R]: JsonSchemaToType<Props[K], Doc>;
} & {
    [K in Exclude<keyof Props, R>]?: JsonSchemaToType<Props[K], Doc>;
} : {
    [K in keyof Props]?: JsonSchemaToType<Props[K], Doc>;
} : S extends {
    type: 'object';
} ? Record<string, unknown> : S extends {
    allOf: ReadonlyArray<infer Items>;
} ? UnionToIntersection<JsonSchemaToType<Items, Doc>> : S extends {
    oneOf: ReadonlyArray<infer Items>;
} ? JsonSchemaToType<Items, Doc> : S extends {
    anyOf: ReadonlyArray<infer Items>;
} ? JsonSchemaToType<Items, Doc> : unknown;
type LowercaseHttpMethod = 'get' | 'put' | 'post' | 'delete' | 'patch' | 'head' | 'options' | 'trace';
type MethodMap = {
    get: 'GET';
    put: 'PUT';
    post: 'POST';
    delete: 'DELETE';
    patch: 'PATCH';
    head: 'HEAD';
    options: 'OPTIONS';
    trace: 'TRACE';
};
type UppercaseMethod<M extends string> = M extends keyof MethodMap ? MethodMap[M] : never;
type PathsWithMethod<T extends OpenApiDocument, M extends LowercaseHttpMethod> = {
    [P in keyof NonNullable<T['paths']> & string]: NonNullable<T['paths']>[P] extends infer PathItem ? M extends keyof PathItem ? PathItem[M] extends object ? P : never : never : never;
}[keyof NonNullable<T['paths']> & string];
type GetOperation<T extends OpenApiDocument, P extends string, M extends LowercaseHttpMethod> = NonNullable<T['paths']>[P] extends infer PathItem ? M extends keyof PathItem ? PathItem[M] : never : never;
type ExtractResponseSchema<Doc extends OpenApiDocument, Op> = Op extends {
    responses: infer R;
} ? R extends {
    '200': infer Resp200;
} ? ResolveSchemaOrRef<Doc, Resp200> extends {
    content: {
        'application/json': {
            schema: infer S;
        };
    };
} ? JsonSchemaToType<S, Doc> : unknown : R extends {
    '201': infer Resp201;
} ? ResolveSchemaOrRef<Doc, Resp201> extends {
    content: {
        'application/json': {
            schema: infer S;
        };
    };
} ? JsonSchemaToType<S, Doc> : unknown : unknown : unknown;
type ExtractRequestBodySchema<Doc extends OpenApiDocument, Op> = Op extends {
    requestBody: infer RB;
} ? ResolveSchemaOrRef<Doc, RB> extends {
    content: {
        'application/json': {
            schema: infer S;
        };
    };
} ? JsonSchemaToType<S, Doc> : never : never;
type ExtractPathParamsFromPath<P extends string> = P extends `${string}{${infer Param}}${infer Rest}` ? {
    [K in Param | keyof ExtractPathParamsFromPath<Rest>]: string;
} : never;
type HasPathParams<P extends string> = P extends `${string}{${string}}${string}` ? true : false;
type ResolvedParam<Doc extends OpenApiDocument, P> = P extends {
    $ref: infer Ref extends string;
} ? ResolveRef<Doc, Ref> : P;
type ExtractQueryParamEntry<Doc extends OpenApiDocument, P> = ResolvedParam<Doc, P> extends {
    in: 'query';
    name: infer N extends string;
    schema: infer S;
} ? {
    [K in N]: JsonSchemaToType<S, Doc>;
} : ResolvedParam<Doc, P> extends {
    in: 'query';
    name: infer N extends string;
} ? {
    [K in N]: string;
} : never;
type BuildQueryParamsFromTuple<Doc extends OpenApiDocument, T> = T extends readonly [infer Head, ...infer Tail] ? [ExtractQueryParamEntry<Doc, Head>] extends [never] ? BuildQueryParamsFromTuple<Doc, Tail> : ExtractQueryParamEntry<Doc, Head> & BuildQueryParamsFromTuple<Doc, Tail> : unknown;
type HasQueryParams<Doc extends OpenApiDocument, T> = T extends readonly [infer Head, ...infer Tail] ? [ExtractQueryParamEntry<Doc, Head>] extends [never] ? HasQueryParams<Doc, Tail> : true : false;
type BuildQueryParams<Doc extends OpenApiDocument, Op> = Op extends {
    parameters: infer Params extends readonly unknown[];
} ? HasQueryParams<Doc, Params> extends true ? BuildQueryParamsFromTuple<Doc, Params> : never : never;
type ExtractTags<Op> = Op extends {
    tags: infer T;
} ? T : never;
type ExtractDeprecated<Op> = Op extends {
    deprecated: true;
} ? true : never;
type ExtractSummary<Op> = Op extends {
    summary: infer S extends string;
} ? S : never;
type ExtractDescription<Op> = Op extends {
    description: infer S extends string;
} ? S : never;
type BuildEndpoint<T extends OpenApiDocument, P extends string, M extends LowercaseHttpMethod> = {
    result: ExtractResponseSchema<T, GetOperation<T, P, M>>;
} & ([ExtractRequestBodySchema<T, GetOperation<T, P, M>>] extends [never] ? unknown : {
    body: ExtractRequestBodySchema<T, GetOperation<T, P, M>>;
}) & (HasPathParams<P> extends true ? {
    url: ExtractPathParamsFromPath<P>;
} : unknown) & ([BuildQueryParams<T, GetOperation<T, P, M>>] extends [never] ? unknown : {
    query: BuildQueryParams<T, GetOperation<T, P, M>>;
}) & ([ExtractTags<GetOperation<T, P, M>>] extends [never] ? unknown : {
    tags: ExtractTags<GetOperation<T, P, M>>;
}) & ([ExtractDeprecated<GetOperation<T, P, M>>] extends [never] ? unknown : {
    deprecated: true;
}) & ([ExtractSummary<GetOperation<T, P, M>>] extends [never] ? unknown : {
    summary: ExtractSummary<GetOperation<T, P, M>>;
}) & ([ExtractDescription<GetOperation<T, P, M>>] extends [never] ? unknown : {
    description: ExtractDescription<GetOperation<T, P, M>>;
});
type EndpointsForMethod<T extends OpenApiDocument, M extends LowercaseHttpMethod> = string extends PathsWithMethod<T, M> ? never : [PathsWithMethod<T, M>] extends [never] ? never : {
    [P in PathsWithMethod<T, M> as ConvertOpenApiPath<P>]: BuildEndpoint<T, P, M>;
};
type CleanObject<T> = {
    [K in keyof T as [T[K]] extends [never] ? never : K]: T[K];
};
/**
 * Extracts a strongly-typed `RestApi` from an OpenAPI document type.
 *
 * Supports `$ref` resolution, `allOf`/`oneOf`/`anyOf` composition, `nullable`, `const`,
 * and metadata extraction (`tags`, `deprecated`, `summary`, `description`).
 *
 * Use with `as const satisfies OpenApiDocument` to get full type inference:
 *
 * @example
 * ```typescript
 * import type { OpenApiDocument, OpenApiToRestApi } from '@furystack/rest'
 * import { createClient } from '@furystack/rest-client-fetch'
 *
 * const apiDoc = { ... } as const satisfies OpenApiDocument
 * type MyApi = OpenApiToRestApi<typeof apiDoc>
 * const client = createClient<MyApi>({ endpointUrl: 'https://api.example.com' })
 * ```
 */
export type OpenApiToRestApi<T extends OpenApiDocument> = CleanObject<{
    [M in LowercaseHttpMethod as [EndpointsForMethod<T, M>] extends [never] ? never : UppercaseMethod<M>]: EndpointsForMethod<T, M>;
}> extends infer R extends RestApi ? R : never;
export {};
//# sourceMappingURL=openapi-to-rest-api.d.ts.map