import { Observable, throwError } from "rxjs";
import { ApolloLink } from "@apollo/client/link";
import { BatchLink } from "@apollo/client/link/batch";
import { checkFetcher, defaultPrinter, fallbackHttpConfig, parseAndCheckHttpResponse, selectHttpOptionsAndBodyInternal, selectURI, } from "@apollo/client/link/http";
import { filterOperationVariables } from "@apollo/client/link/utils";
import { __DEV__ } from "@apollo/client/utilities/environment";
import { compact } from "@apollo/client/utilities/internal";
import { maybe } from "@apollo/client/utilities/internal/globals";
const backupFetch = maybe(() => fetch);
/**
 * `BaseBatchHttpLink` is a terminating link that batches array of individual
 * GraphQL operations into a single HTTP request that's sent to a single GraphQL
 * endpoint. It serves as a base link to `BatchHttpLink`.
 *
 * @remarks
 *
 * > [!NOTE]
 * > Prefer using `BatchHttpLink` over `BaseBatchHttpLink`. Use
 * > `BaseBatchHttpLink` when you need to disable client awareness features and
 * > would like to tree-shake the implementation of `ClientAwarenessLink` out
 * > of your app bundle.
 *
 * @example
 *
 * ```ts
 * import { BaseBatchHttpLink } from "@apollo/client/link/batch-http";
 *
 * const link = new BaseBatchHttpLink({
 *   uri: "http://localhost:4000/graphql",
 *   batchMax: 5, // No more than 5 operations per batch
 *   batchInterval: 20, // Wait no more than 20ms after first batched operation
 * });
 * ```
 */
export class BaseBatchHttpLink extends ApolloLink {
    batchDebounce;
    batchInterval;
    batchMax;
    batcher;
    constructor(options = {}) {
        super();
        let { uri = "/graphql", 
        // use default global fetch if nothing is passed in
        fetch: preferredFetch, print = defaultPrinter, includeExtensions, preserveHeaderCase, batchInterval, batchDebounce, batchMax, batchKey, includeUnusedVariables = false, ...requestOptions } = options;
        if (__DEV__) {
            // Make sure at least one of preferredFetch, window.fetch, or backupFetch
            // is defined, so requests won't fail at runtime.
            checkFetcher(preferredFetch || backupFetch);
        }
        const linkConfig = {
            http: compact({ includeExtensions, preserveHeaderCase }),
            options: requestOptions.fetchOptions,
            credentials: requestOptions.credentials,
            headers: requestOptions.headers,
        };
        this.batchDebounce = batchDebounce;
        this.batchInterval = batchInterval || 10;
        this.batchMax = batchMax || 10;
        const batchHandler = (operations) => {
            const chosenURI = selectURI(operations[0], uri);
            const context = operations[0].getContext();
            const contextConfig = {
                http: context.http,
                options: context.fetchOptions,
                credentials: context.credentials,
                headers: context.headers,
            };
            //uses fallback, link, and then context to build options
            const optsAndBody = operations.map((operation) => {
                const result = selectHttpOptionsAndBodyInternal(operation, print, fallbackHttpConfig, linkConfig, contextConfig);
                if (result.body.variables && !includeUnusedVariables) {
                    result.body.variables = filterOperationVariables(result.body.variables, operation.query);
                }
                return result;
            });
            const loadedBody = optsAndBody.map(({ body }) => body);
            const options = optsAndBody[0].options;
            // There's no spec for using GET with batches.
            if (options.method === "GET") {
                return throwError(() => new Error("apollo-link-batch-http does not support GET requests"));
            }
            try {
                options.body = JSON.stringify(loadedBody);
            }
            catch (parseError) {
                return throwError(() => parseError);
            }
            let controller;
            if (!options.signal && typeof AbortController !== "undefined") {
                controller = new AbortController();
                options.signal = controller.signal;
            }
            return new Observable((observer) => {
                // Prefer BatchHttpLink.Options.fetch (preferredFetch) if provided, and
                // otherwise fall back to the *current* global window.fetch function
                // (see issue #7832), or (if all else fails) the backupFetch function we
                // saved when this module was first evaluated. This last option protects
                // against the removal of window.fetch, which is unlikely but not
                // impossible.
                const currentFetch = preferredFetch || maybe(() => fetch) || backupFetch;
                currentFetch(chosenURI, options)
                    .then((response) => {
                    // Make the raw response available in the context.
                    operations.forEach((operation) => operation.setContext({ response }));
                    return response;
                })
                    .then(parseAndCheckHttpResponse(operations))
                    .then((result) => {
                    controller = undefined;
                    // we have data and can send it to back up the link chain
                    observer.next(result);
                    observer.complete();
                    return result;
                })
                    .catch((err) => {
                    controller = undefined;
                    observer.error(err);
                });
                return () => {
                    // XXX support canceling this request
                    // https://developers.google.com/web/updates/2017/09/abortable-fetch
                    if (controller)
                        controller.abort();
                };
            });
        };
        batchKey =
            batchKey ||
                ((operation) => {
                    const context = operation.getContext();
                    const contextConfig = {
                        http: context.http,
                        options: context.fetchOptions,
                        credentials: context.credentials,
                        headers: context.headers,
                    };
                    //may throw error if config not serializable
                    return selectURI(operation, uri) + JSON.stringify(contextConfig);
                });
        this.batcher = new BatchLink({
            batchDebounce: this.batchDebounce,
            batchInterval: this.batchInterval,
            batchMax: this.batchMax,
            batchKey,
            batchHandler,
        });
    }
    request(operation, forward) {
        return this.batcher.request(operation, forward);
    }
}
//# sourceMappingURL=BaseBatchHttpLink.js.map