import * as React from "react";
import { NetworkStatus } from "@apollo/client";
import { canonicalStringify } from "@apollo/client/cache";
import { getSuspenseCache } from "@apollo/client/react/internal";
import { __DEV__ } from "@apollo/client/utilities/environment";
import { invariant } from "@apollo/client/utilities/invariant";
import { skipToken } from "./constants.js";
import { __use, useDeepMemo, wrapHook } from "./internal/index.js";
import { useApolloClient } from "./useApolloClient.js";
export function useSuspenseQuery(query, options = {}) {
    return wrapHook("useSuspenseQuery", 
    // eslint-disable-next-line react-compiler/react-compiler
    useSuspenseQuery_, useApolloClient(typeof options === "object" ? options.client : undefined))(query, options);
}
function useSuspenseQuery_(query, options) {
    const client = useApolloClient(options.client);
    const suspenseCache = getSuspenseCache(client);
    const watchQueryOptions = useWatchQueryOptions({
        client,
        query,
        options,
    });
    const { fetchPolicy, variables } = watchQueryOptions;
    const { queryKey = [] } = options;
    const cacheKey = [
        query,
        canonicalStringify(variables),
        ...[].concat(queryKey),
    ];
    const queryRef = suspenseCache.getQueryRef(cacheKey, () => client.watchQuery(watchQueryOptions));
    let [current, setPromise] = React.useState([queryRef.key, queryRef.promise]);
    // This saves us a re-execution of the render function when a variable changed.
    if (current[0] !== queryRef.key) {
        // eslint-disable-next-line react-compiler/react-compiler
        current[0] = queryRef.key;
        current[1] = queryRef.promise;
    }
    let promise = current[1];
    if (queryRef.didChangeOptions(watchQueryOptions)) {
        current[1] = promise = queryRef.applyOptions(watchQueryOptions);
    }
    React.useEffect(() => {
        const dispose = queryRef.retain();
        const removeListener = queryRef.listen((promise) => {
            setPromise([queryRef.key, promise]);
        });
        return () => {
            removeListener();
            dispose();
        };
    }, [queryRef]);
    const skipResult = React.useMemo(() => {
        const error = queryRef.result.error;
        const complete = !!queryRef.result.data;
        return {
            loading: false,
            data: queryRef.result.data,
            networkStatus: error ? NetworkStatus.error : NetworkStatus.ready,
            error,
            complete,
            partial: !complete,
        };
    }, [queryRef.result]);
    const result = fetchPolicy === "standby" ? skipResult : __use(promise);
    const fetchMore = React.useCallback((options) => {
        const promise = queryRef.fetchMore(options);
        setPromise([queryRef.key, queryRef.promise]);
        return promise;
    }, [queryRef]);
    const refetch = React.useCallback((variables) => {
        const promise = queryRef.refetch(variables);
        setPromise([queryRef.key, queryRef.promise]);
        return promise;
    }, [queryRef]);
    // TODO: The internalQueryRef doesn't have TVariables' type information so we have to cast it here
    const subscribeToMore = queryRef.observable
        .subscribeToMore;
    return React.useMemo(() => {
        return {
            client,
            data: result.data,
            error: result.error,
            networkStatus: result.networkStatus,
            fetchMore,
            refetch,
            subscribeToMore,
        };
    }, [client, fetchMore, refetch, result, subscribeToMore]);
}
function validateOptions(options) {
    const { fetchPolicy, returnPartialData } = options;
    validateFetchPolicy(fetchPolicy);
    validatePartialDataReturn(fetchPolicy, returnPartialData);
}
function validateFetchPolicy(fetchPolicy = "cache-first") {
    const supportedFetchPolicies = [
        "cache-first",
        "network-only",
        "no-cache",
        "cache-and-network",
    ];
    invariant(supportedFetchPolicies.includes(fetchPolicy), 37, fetchPolicy);
}
function validatePartialDataReturn(fetchPolicy, returnPartialData) {
    if (fetchPolicy === "no-cache" && returnPartialData) {
        __DEV__ && invariant.warn(38);
    }
}
export function useWatchQueryOptions({ client, query, options, }) {
    return useDeepMemo(() => {
        if (options === skipToken) {
            return { query, fetchPolicy: "standby" };
        }
        const fetchPolicy = options.fetchPolicy ||
            client.defaultOptions.watchQuery?.fetchPolicy ||
            "cache-first";
        const watchQueryOptions = {
            ...options,
            fetchPolicy,
            query,
            notifyOnNetworkStatusChange: false,
            nextFetchPolicy: void 0,
        };
        if (__DEV__) {
            validateOptions(watchQueryOptions);
        }
        // Assign the updated fetch policy after our validation since `standby` is
        // not a supported fetch policy on its own without the use of `skip`.
        if (options.skip) {
            watchQueryOptions.fetchPolicy = "standby";
        }
        return watchQueryOptions;
    }, [client, options, query]);
}
//# sourceMappingURL=useSuspenseQuery.js.map
