// This file is auto-generated by @hey-api/openapi-ts
import { createSseClient } from "../core/serverSentEvents.gen.js";
import { getValidRequestBody } from "../core/utils.gen.js";
import { buildUrl, createConfig, createInterceptors, getParseAs, mergeConfigs, mergeHeaders, setAuthParams, } from "./utils.gen.js";
export const createClient = (config = {}) => {
    let _config = mergeConfigs(createConfig(), config);
    const getConfig = () => ({ ..._config });
    const setConfig = (config) => {
        _config = mergeConfigs(_config, config);
        return getConfig();
    };
    const interceptors = createInterceptors();
    const beforeRequest = async (options) => {
        const opts = {
            ..._config,
            ...options,
            fetch: options.fetch ?? _config.fetch ?? globalThis.fetch,
            headers: mergeHeaders(_config.headers, options.headers),
            serializedBody: undefined,
        };
        if (opts.security) {
            await setAuthParams({
                ...opts,
                security: opts.security,
            });
        }
        if (opts.requestValidator) {
            await opts.requestValidator(opts);
        }
        if (opts.body !== undefined && opts.bodySerializer) {
            opts.serializedBody = opts.bodySerializer(opts.body);
        }
        // remove Content-Type header if body is empty to avoid sending invalid requests
        if (opts.body === undefined || opts.serializedBody === "") {
            opts.headers.delete("Content-Type");
        }
        const url = buildUrl(opts);
        return { opts, url };
    };
    const request = async (options) => {
        // @ts-expect-error
        const { opts, url } = await beforeRequest(options);
        const requestInit = {
            redirect: "follow",
            ...opts,
            body: getValidRequestBody(opts),
        };
        let request = new Request(url, requestInit);
        for (const fn of interceptors.request.fns) {
            if (fn) {
                request = await fn(request, opts);
            }
        }
        // fetch must be assigned here, otherwise it would throw the error:
        // TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation
        const _fetch = opts.fetch;
        let response;
        try {
            response = await _fetch(request);
        }
        catch (error) {
            // Handle fetch exceptions (AbortError, network errors, etc.)
            let finalError = error;
            for (const fn of interceptors.error.fns) {
                if (fn) {
                    finalError = (await fn(error, undefined, request, opts));
                }
            }
            finalError = finalError || {};
            if (opts.throwOnError) {
                throw finalError;
            }
            // Return error response
            return opts.responseStyle === "data"
                ? undefined
                : {
                    error: finalError,
                    request,
                    response: undefined,
                };
        }
        for (const fn of interceptors.response.fns) {
            if (fn) {
                response = await fn(response, request, opts);
            }
        }
        const result = {
            request,
            response,
        };
        if (response.ok) {
            const parseAs = (opts.parseAs === "auto" ? getParseAs(response.headers.get("Content-Type")) : opts.parseAs) ?? "json";
            if (response.status === 204 || response.headers.get("Content-Length") === "0") {
                let emptyData;
                switch (parseAs) {
                    case "arrayBuffer":
                    case "blob":
                    case "text":
                        emptyData = await response[parseAs]();
                        break;
                    case "formData":
                        emptyData = new FormData();
                        break;
                    case "stream":
                        emptyData = response.body;
                        break;
                    case "json":
                    default:
                        emptyData = {};
                        break;
                }
                return opts.responseStyle === "data"
                    ? emptyData
                    : {
                        data: emptyData,
                        ...result,
                    };
            }
            let data;
            switch (parseAs) {
                case "arrayBuffer":
                case "blob":
                case "formData":
                case "text":
                    data = await response[parseAs]();
                    break;
                case "json": {
                    // Some servers return 200 with no Content-Length and empty body.
                    // response.json() would throw; read as text and parse if non-empty.
                    const text = await response.text();
                    data = text ? JSON.parse(text) : {};
                    break;
                }
                case "stream":
                    return opts.responseStyle === "data"
                        ? response.body
                        : {
                            data: response.body,
                            ...result,
                        };
            }
            if (parseAs === "json") {
                if (opts.responseValidator) {
                    await opts.responseValidator(data);
                }
                if (opts.responseTransformer) {
                    data = await opts.responseTransformer(data);
                }
            }
            return opts.responseStyle === "data"
                ? data
                : {
                    data,
                    ...result,
                };
        }
        const textError = await response.text();
        let jsonError;
        try {
            jsonError = JSON.parse(textError);
        }
        catch {
            // noop
        }
        const error = jsonError ?? textError;
        let finalError = error;
        for (const fn of interceptors.error.fns) {
            if (fn) {
                finalError = (await fn(error, response, request, opts));
            }
        }
        finalError = finalError || {};
        if (opts.throwOnError) {
            throw finalError;
        }
        // TODO: we probably want to return error and improve types
        return opts.responseStyle === "data"
            ? undefined
            : {
                error: finalError,
                ...result,
            };
    };
    const makeMethodFn = (method) => (options) => request({ ...options, method });
    const makeSseFn = (method) => async (options) => {
        const { opts, url } = await beforeRequest(options);
        return createSseClient({
            ...opts,
            body: opts.body,
            headers: opts.headers,
            method,
            onRequest: async (url, init) => {
                let request = new Request(url, init);
                for (const fn of interceptors.request.fns) {
                    if (fn) {
                        request = await fn(request, opts);
                    }
                }
                return request;
            },
            serializedBody: getValidRequestBody(opts),
            url,
        });
    };
    return {
        buildUrl,
        connect: makeMethodFn("CONNECT"),
        delete: makeMethodFn("DELETE"),
        get: makeMethodFn("GET"),
        getConfig,
        head: makeMethodFn("HEAD"),
        interceptors,
        options: makeMethodFn("OPTIONS"),
        patch: makeMethodFn("PATCH"),
        post: makeMethodFn("POST"),
        put: makeMethodFn("PUT"),
        request,
        setConfig,
        sse: {
            connect: makeSseFn("CONNECT"),
            delete: makeSseFn("DELETE"),
            get: makeSseFn("GET"),
            head: makeSseFn("HEAD"),
            options: makeSseFn("OPTIONS"),
            patch: makeSseFn("PATCH"),
            post: makeSseFn("POST"),
            put: makeSseFn("PUT"),
            trace: makeSseFn("TRACE"),
        },
        trace: makeMethodFn("TRACE"),
    };
};
