import { jsx as _jsx } from "react/jsx-runtime";
import React from "react";
import { initHandleDataSync } from "../browser/react/use-handle.js";
import { initSegmentsSync } from "../browser/react/use-segments.js";
import { initThemeConfigSync } from "../theme/theme-context.js";
import { ThemeProvider } from "../theme/ThemeProvider.js";
import { NavigationStoreContext } from "../browser/react/context.js";
/**
 * Consume an async generator and return a Promise that resolves with the final value.
 * Used for SSR where we need to await all handle data before rendering.
 */
async function consumeAsyncGenerator(generator) {
    let lastData = {};
    for await (const data of generator) {
        lastData = data;
    }
    return lastData;
}
/**
 * Create a minimal event controller for SSR.
 * This provides the correct pathname so useNavigation returns the right value during SSR.
 */
function createSsrEventController(pathname) {
    const location = new URL(pathname, "http://localhost");
    const state = {
        state: "idle",
        isStreaming: false,
        location,
        pendingUrl: null,
        inflightActions: [],
    };
    return {
        getState: () => state,
        subscribe: () => () => { },
        getActionState: () => ({
            state: "idle",
            actionId: null,
            payload: null,
            error: null,
            result: null,
        }),
        subscribeToAction: () => () => { },
        subscribeToHandles: () => () => { },
        setHandleData: () => { },
        getHandleState: () => ({ data: {}, segmentOrder: [] }),
        setLocation: () => { },
        startNavigation: () => {
            throw new Error("Navigation not supported during SSR");
        },
        abortNavigation: () => { },
        startAction: () => {
            throw new Error("Actions not supported during SSR");
        },
        abortAllActions: () => { },
        getCurrentNavigation: () => null,
        getInflightActions: () => new Map(),
    };
}
/**
 * Create an SSR handler that converts RSC streams to HTML.
 *
 * @example
 * ```tsx
 * import { createSSRHandler } from "rsc-router/ssr";
 * import { createFromReadableStream } from "@vitejs/plugin-rsc/ssr";
 * import { renderToReadableStream } from "react-dom/server.edge";
 * import { injectRSCPayload } from "rsc-html-stream/server";
 *
 * export const renderHTML = createSSRHandler({
 *   createFromReadableStream,
 *   renderToReadableStream,
 *   injectRSCPayload,
 *   loadBootstrapScriptContent: () =>
 *     import.meta.viteRsc.loadBootstrapScriptContent("index"),
 * });
 * ```
 */
export function createSSRHandler(deps) {
    const { createFromReadableStream, renderToReadableStream, injectRSCPayload, loadBootstrapScriptContent, onError, } = deps;
    /**
     * Render RSC stream to HTML stream
     *
     * @param rscStream - The RSC stream to render
     * @param options - Optional render options including formState for useActionState and nonce for CSP
     */
    return async function renderHTML(rscStream, options) {
        const { nonce, formState } = options ?? {};
        try {
            // Tee the stream:
            // - rscStream1: For SSR rendering (deserialize to React VDOM)
            // - rscStream2: For browser hydration (inject as __FLIGHT_DATA__)
            const [rscStream1, rscStream2] = rscStream.tee();
            // Deserialize RSC stream to React tree
            let payload;
            let handlesPromise;
            let ssrContextValue;
            function SsrRoot() {
                payload ?? (payload = createFromReadableStream(rscStream1));
                const resolved = React.use(payload);
                // Initialize segments state before children render (for useSegments hook)
                initSegmentsSync(resolved.metadata?.matched, resolved.metadata?.pathname);
                // Initialize theme config for MetaTags to render theme script
                const themeConfig = resolved.metadata?.themeConfig ?? null;
                initThemeConfigSync(themeConfig);
                // Await handles and initialize state before children render
                // The handles property is an async generator that yields on each push
                // Memoize the promise since async generators can only be iterated once
                if (resolved.metadata?.handles) {
                    handlesPromise ?? (handlesPromise = consumeAsyncGenerator(resolved.metadata.handles));
                    const handleData = React.use(handlesPromise);
                    initHandleDataSync(handleData, resolved.metadata.matched);
                }
                // Create SSR context with correct pathname for useNavigation
                ssrContextValue ?? (ssrContextValue = {
                    store: null,
                    eventController: createSsrEventController(resolved.metadata?.pathname ?? "/"),
                    navigate: async () => { },
                    refresh: async () => { },
                });
                // Build content tree with all necessary providers
                // Order must match NavigationProvider: NavigationStoreContext > ThemeProvider > content
                let content = resolved.root;
                // Wrap content with ThemeProvider if theme is enabled
                if (themeConfig) {
                    content = (_jsx(ThemeProvider, { config: themeConfig, initialTheme: resolved.metadata?.initialTheme, children: content }));
                }
                // Wrap with NavigationStoreContext for useNavigation hook
                return (_jsx(NavigationStoreContext.Provider, { value: ssrContextValue, children: content }));
            }
            // Get bootstrap script content
            const bootstrapScriptContent = await loadBootstrapScriptContent();
            // Render React tree to HTML stream
            // Pass formState for useActionState progressive enhancement if provided
            // Pass nonce for CSP if provided
            const htmlStream = await renderToReadableStream(_jsx(SsrRoot, {}), {
                bootstrapScriptContent,
                formState,
                nonce,
            });
            // Inject RSC payload into HTML as <script nonce="...">__FLIGHT_DATA__</script>
            return htmlStream.pipeThrough(injectRSCPayload(rscStream2, { nonce }));
        }
        catch (error) {
            // Invoke onError callback if provided
            if (onError) {
                const errorObj = error instanceof Error ? error : new Error(String(error));
                try {
                    onError(errorObj, { phase: "rendering" });
                }
                catch (callbackError) {
                    console.error("[SSRHandler.onError] Callback error:", callbackError);
                }
            }
            throw error;
        }
    };
}
//# sourceMappingURL=index.js.map