import React from 'react';
import { DataTransportProviderImplementation } from '@apollo/client-react-streaming';

/**
 * @public
 */
interface HydrationContextOptions {
    /**
     * Props that will be passed down to `script` tags that will be used to transport
     * data to the browser.
     * Can e.g. be used to add a `nonce`.
     */
    extraScriptProps?: ScriptProps;
}
type SerializableProps<T> = Pick<T, {
    [K in keyof T]: T[K] extends string | number | boolean | undefined | null ? K : never;
}[keyof T]>;
type ScriptProps = SerializableProps<React.ScriptHTMLAttributes<HTMLScriptElement>>;

interface ManualDataTransportOptions {
    /**
     * A hook that allows for insertion into the stream.
     * Will only be called during SSR, doesn't need to actiually return something otherwise.
     */
    useInsertHtml(): (callbacks: () => React.ReactNode) => void;
    /**
     * Prepare data for injecting into the stream by converting it into a string that can be parsed as JavaScript by the browser.
     * Could e.g. be `SuperJSON.stringify` or `serialize-javascript`.
     * The default implementation act like a JSON.stringify that preserves `undefined`, but not do much on top of that.
     */
    stringifyForStream?: (value: any) => string;
    /**
     * If necessary, additional deserialization steps that need to be applied on top of executing the result of `stringifyForStream` in the browser.
     * Could e.g. be `SuperJSON.deserialize`. (Not needed in the case of using `serialize-javascript`)
     */
    reviveFromStream?: (value: any) => any;
    /**
     * **Read the whole comment before using this option!**
     *
     * If `true`, the `useStaticValueRef` hook will not transport values over to the client.
     * This hook is used to transport the values of hook calls during SSR to the client, to ensure that
     * the client will rehydrate with the exact same values as it rendered on the server.
     *
     * This mechanism is in place to prevent hydration mismatches as described in
     * https://github.com/apollographql/apollo-client-integrations/blob/pr/RFC-2/RFC.md#challenges-of-a-client-side-cache-in-streaming-ssr
     * (first graph of the "Challenges of a client-side cache in streaming SSR" section).
     *
     * Setting this value to `true` will save on data transported over the wire, but comes with the risk
     * of hydration mismatches.
     * Strongly discouraged with older React versions, as hydration mismatches there will likely crash
     * the application, setting this to `true` might be okay with React 19, which is much better at recovering
     * from hydration mismatches - but it still comes with a risk.
     * When enabling this, you should closely monitor error reporting and user feedback.
     */
    dangerous_disableHookValueTransportation?: boolean;
}
/**
 * > This export is only available in React Client Components
 *
 * Creates a "manual" Data Transport, to be used with `WrapApolloProvider`.
 *
 * @remarks
 *
 * ### Drawbacks
 *
 * While this Data Transport enables streaming SSR, it has some conceptual drawbacks:
 *
 * - It does not have a way of keeping your connection open if your application already finished, but there are still ongoing queries that might need to be transported over.
 *   - This can happen if a component renders `useBackgroundQuery`, but does not read the `queryRef` with `useReadQuery`
 *   - These "cut off" queries will be restarted in the browser once the browser's `load` event happens
 * - If the `useInsertHtml` doesn't immediately flush data to the browser, the browser might already attain "newer" data through queries triggered by user interaction.
 *   - This delayed behaviour is the case with the Next.js `ServerInsertedHTMLContext` and in the example Vite implementation.
 *   - In this, case, older data from the server might overwrite newer data in the browser. This is minimized by simulating ongoing queries in the browser once the information of a started query is transported over.
 *     If the browser would try to trigger the exact same query, query deduplication would make the browser wait for the server query to resolve instead.
 * - For more timing-related details, see https://github.com/apollographql/apollo-client-integrations/pull/9
 *
 * To fully work around these drawbacks, React needs to add "data injection into the stream" to it's public API, which is not the case today.
 * We provide an [example with a patched React version](https://github.com/apollographql/apollo-client-integrations/blob/main/integration-test/experimental-react) to showcase how that could look.
 *
 * @example
 * For usage examples, see the implementation of the `@apollo/client-integration-nextjs`
 * [`ApolloNextAppProvider`](https://github.com/apollographql/apollo-client-integrations/blob/c0715a05cf8ca29a3cbb9ce294cdcbc5ce251b2e/packages/experimental-nextjs-app-support/src/ApolloNextAppProvider.ts)
 *
 * ```tsx
 * export const ApolloNextAppProvider = WrapApolloProvider(
 *   buildManualDataTransport({
 *     useInsertHtml() {
 *       const insertHtml = useContext(ServerInsertedHTMLContext);
 *       if (!insertHtml) {
 *         throw new Error(
 *           "ApolloNextAppProvider cannot be used outside of the Next App Router!"
 *         );
 *       }
 *       return insertHtml;
 *     },
 *   })
 * );
 * ```
 *
 * @example
 * Another usage example is our integration example with Vite and streaming SSR, which you can find at https://github.com/apollographql/apollo-client-integrations/tree/main/integration-test/vite-streaming
 *
 * @public
 */
declare const buildManualDataTransport: (args: ManualDataTransportOptions) => DataTransportProviderImplementation<HydrationContextOptions>;

type ValidQueueKeys = {
    [K in keyof Window]-?: NonNullable<Window[K]> extends {
        push(...args: any[]): any;
    } ? K : never;
}[keyof Window];
/**
 * Registers a queue that can be filled with data before it has actually been initialized with this function.
 * Before calling this function, `window[key]` can just be handled as an array of data.
 * When calling this funcation, all accumulated data will be passed to the callback.
 * After calling this function, `window[key]` will be an object with a `push` method that will call the callback with the data.
 *
 * @public
 */
declare function registerLateInitializingQueue<K extends ValidQueueKeys>(key: K, callback: (data: Parameters<NonNullable<Window[K]>["push"]>[0]) => void): void;

/**
 * > This export is only available in React Client Components
 *
 * Resets the singleton instances created for the Apollo SSR data transport and caches.
 *
 * To be used in testing only, like
 * ```ts
 * afterEach(resetManualSSRApolloSingletons);
 * ```
 *
 * @public
 */
declare function resetManualSSRApolloSingletons(): void;

export { type HydrationContextOptions, buildManualDataTransport, registerLateInitializingQueue, resetManualSSRApolloSingletons };
