import type { Hono } from '../hono';
import type { HonoBase } from '../hono-base';
import type { Endpoint, ResponseFormat, Schema } from '../types';
import type { StatusCode, SuccessStatusCode } from '../utils/http-status';
import type { HasRequiredKeys } from '../utils/types';
type HonoRequest = (typeof Hono.prototype)['request'];
export type ClientRequestOptions<T = unknown> = {
    fetch?: typeof fetch | HonoRequest;
    webSocket?: (...args: ConstructorParameters<typeof WebSocket>) => WebSocket;
    /**
     * Standard `RequestInit`, caution that this take highest priority
     * and could be used to overwrite things that Hono sets for you, like `body | method | headers`.
     *
     * If you want to add some headers, use in `headers` instead of `init`
     */
    init?: RequestInit;
} & (keyof T extends never ? {
    headers?: Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>);
} : {
    headers: T | (() => T | Promise<T>);
});
export type ClientRequest<S extends Schema> = {
    [M in keyof S]: S[M] extends Endpoint & {
        input: infer R;
    } ? R extends object ? HasRequiredKeys<R> extends true ? (args: R, options?: ClientRequestOptions) => Promise<ClientResponseOfEndpoint<S[M]>> : (args?: R, options?: ClientRequestOptions) => Promise<ClientResponseOfEndpoint<S[M]>> : never : never;
} & {
    $url: (arg?: S[keyof S] extends {
        input: infer R;
    } ? R extends {
        param: infer P;
    } ? R extends {
        query: infer Q;
    } ? {
        param: P;
        query: Q;
    } : {
        param: P;
    } : R extends {
        query: infer Q;
    } ? {
        query: Q;
    } : {} : {}) => URL;
} & (S['$get'] extends {
    outputFormat: 'ws';
} ? S['$get'] extends {
    input: infer I;
} ? {
    $ws: (args?: I) => WebSocket;
} : {} : {});
type ClientResponseOfEndpoint<T extends Endpoint = Endpoint> = T extends {
    output: infer O;
    outputFormat: infer F;
    status: infer S;
} ? ClientResponse<O, S extends number ? S : never, F extends ResponseFormat ? F : never> : never;
export interface ClientResponse<T, U extends number = StatusCode, F extends ResponseFormat = ResponseFormat> extends globalThis.Response {
    readonly body: ReadableStream | null;
    readonly bodyUsed: boolean;
    ok: U extends SuccessStatusCode ? true : U extends Exclude<StatusCode, SuccessStatusCode> ? false : boolean;
    status: U;
    statusText: string;
    headers: Headers;
    url: string;
    redirect(url: string, status: number): Response;
    clone(): Response;
    json(): F extends 'text' ? Promise<never> : F extends 'json' ? Promise<T> : Promise<unknown>;
    text(): F extends 'text' ? (T extends string ? Promise<T> : Promise<never>) : Promise<string>;
    blob(): Promise<Blob>;
    formData(): Promise<FormData>;
    arrayBuffer(): Promise<ArrayBuffer>;
}
export interface Response extends ClientResponse<unknown> {
}
export type Fetch<T> = (args?: InferRequestType<T>, opt?: ClientRequestOptions) => Promise<ClientResponseOfEndpoint<InferEndpointType<T>>>;
type InferEndpointType<T> = T extends (args: infer R, options: any | undefined) => Promise<infer U> ? U extends ClientResponse<infer O, infer S, infer F> ? {
    input: NonNullable<R>;
    output: O;
    outputFormat: F;
    status: S;
} extends Endpoint ? {
    input: NonNullable<R>;
    output: O;
    outputFormat: F;
    status: S;
} : never : never : never;
export type InferResponseType<T, U extends StatusCode = StatusCode> = InferResponseTypeFromEndpoint<InferEndpointType<T>, U>;
type InferResponseTypeFromEndpoint<T extends Endpoint, U extends StatusCode> = T extends {
    output: infer O;
    status: infer S;
} ? S extends U ? O : never : never;
export type InferRequestType<T> = T extends (args: infer R, options: any | undefined) => Promise<ClientResponse<unknown>> ? NonNullable<R> : never;
export type InferRequestOptionsType<T> = T extends (args: any, options: infer R) => Promise<ClientResponse<unknown>> ? NonNullable<R> : never;
/**
 * Filter a ClientResponse type so it only includes responses of specific status codes.
 */
export type FilterClientResponseByStatusCode<T extends ClientResponse<any, any, any>, U extends number = StatusCode> = T extends ClientResponse<infer RT, infer RC, infer RF> ? RC extends U ? ClientResponse<RT, RC, RF> : never : never;
type PathToChain<Path extends string, E extends Schema, Original extends string = Path> = Path extends `/${infer P}` ? PathToChain<P, E, Path> : Path extends `${infer P}/${infer R}` ? {
    [K in P]: PathToChain<R, E, Original>;
} : {
    [K in Path extends '' ? 'index' : Path]: ClientRequest<E extends Record<string, unknown> ? E[Original] : never>;
};
export type Client<T> = T extends HonoBase<any, infer S, any> ? S extends Record<infer K, Schema> ? K extends string ? PathToChain<K, S> : never : never : never;
export type Callback = (opts: CallbackOptions) => unknown;
interface CallbackOptions {
    path: string[];
    args: any[];
}
export type ObjectType<T = unknown> = {
    [key: string]: T;
};
export {};
