import { defaultAssertRetryableResponse, formatResponse, useRetryableFetch, } from '@finalytic/utils';
import { createClient } from 'gqty';
import { hasuraGraphqlUri, hasuraQueryTypeHeader } from '../constants';
import { generatedSchema, scalarsEnumsHash, } from '../gqty';
import { wrapGraphQLClient } from '../wrap';
import { createHeadersFactory } from './headers';
import { createHasuraSubscriptionsClient } from './subscription';
const retryablesErrors = [
    'deadlock detected',
    'fetch failed',
    'Unexpected token',
    'operation exceeded the time limit',
];
function isRetryableResponse(json) {
    if (!json || typeof json !== 'object')
        return null;
    const message = JSON.stringify(json.errors || {});
    if (!message)
        return null;
    return retryablesErrors.find((e) => message?.includes(e));
}
export const defaultAssertHasuraRetryableResponse = async (arg) => {
    const defaultResponse = await defaultAssertRetryableResponse(arg);
    if (defaultResponse && 'retryable' in defaultResponse)
        return defaultResponse;
    const json = await formatResponse(arg.response).catch(() => null);
    arg.response.json = () => json;
    const retryableResponse = isRetryableResponse(json);
    if (retryableResponse) {
        return {
            retryable: true,
            errorMessage: retryableResponse,
            errorDetails: json,
        };
    }
};
export function useHasuraClient({ uri = hasuraGraphqlUri, secret, auditUserId, accessToken, subscriptions, headers, credentials, ...retryableFetchArgs } = {}) {
    const getHeaders = createHeadersFactory({
        accessToken,
        auditUserId,
        headers,
        secret,
    });
    // Create the subscriptions client if needed
    const subscriptionsClient = createHasuraSubscriptionsClient(uri, getHeaders, subscriptions !== false);
    const fetchFn = retryableFetchArgs.fetch || fetch;
    const attempts = retryableFetchArgs.attempts === undefined ? 3 : retryableFetchArgs.attempts;
    const retryableFetch = attempts > 1
        ? useRetryableFetch({
            assertResponse: defaultAssertHasuraRetryableResponse,
            ...retryableFetchArgs,
            attempts,
        })
        : undefined;
    // Create the GQty client
    const client = () => createClient({
        schema: generatedSchema,
        scalarsEnumsHash,
        async queryFetcher(query, variables, fetchOptions) {
            const getRequest = async () => {
                const headers = await getHeaders({
                    'Content-Type': 'application/json',
                });
                if (fetchOptions?.headers) {
                    for (const key in fetchOptions.headers) {
                        headers[key] = fetchOptions.headers[key];
                    }
                }
                const isMutation = query.includes('mutation');
                headers[hasuraQueryTypeHeader] = isMutation ? 'mutation' : 'query';
                return new Request(uri, {
                    method: 'POST',
                    credentials,
                    headers,
                    body: JSON.stringify({
                        query,
                        variables,
                    }),
                });
            };
            const response = retryableFetch
                ? await retryableFetch(getRequest)
                : await fetchFn(await getRequest());
            const json = await response.json();
            return json;
        },
        subscriptionsClient,
        normalization: false,
        defaults: {
            resolved: {
                noCache: true,
                retry: false, // We're handling retries ourselves in queryFetcher
            },
        },
        retry: { maxRetries: 0 },
    });
    return wrapGraphQLClient(client, subscriptionsClient);
}
