import { getRequestContext } from "../../server/request-context.js";
import { getRouterContext } from "../router-context.js";
import { debugLog, debugWarn } from "../logging.js";
/**
 * Creates cache store middleware
 *
 * Observes all segments passing through and stores them in cache after pipeline completes.
 * Handles proactive caching for null-component segments.
 */
export function withCacheStore(ctx, state) {
    return async function* (source) {
        const pipelineStart = performance.now();
        const ms = ctx.metricsStore;
        // Collect all segments while passing them through
        const allSegments = [];
        for await (const segment of source) {
            allSegments.push(segment);
            yield segment;
        }
        // Skip caching if:
        // 1. Cache miss but cache scope is disabled
        // 2. This is an action (actions don't cache)
        // 3. Cache was already hit (no need to re-cache)
        // 4. Non-GET request (only cache GET requests)
        if (!ctx.cacheScope?.enabled ||
            ctx.isAction ||
            state.cacheHit ||
            ctx.request.method !== "GET") {
            if (ms) {
                ms.metrics.push({ label: "pipeline:cache-store", duration: performance.now() - pipelineStart, startTime: pipelineStart - ms.requestStart });
            }
            return;
        }
        const { createHandlerContext, setupLoaderAccessSilent, resolveAllSegments, resolveInterceptEntry, } = getRouterContext();
        // Combine main segments with intercept segments
        const allSegmentsToCache = [...allSegments, ...state.interceptSegments];
        // Check if any non-loader segments have null components
        // This happens when client already had those segments (partial navigation)
        const hasNullComponents = allSegmentsToCache.some((s) => s.component === null && s.type !== "loader");
        const requestCtx = getRequestContext();
        if (!requestCtx)
            return;
        const cacheScope = ctx.cacheScope;
        // Register onResponse callback to skip caching for non-200 responses
        // Note: error/notFound status codes are set elsewhere (not caching-specific)
        requestCtx.onResponse((response) => {
            // Only cache successful responses
            if (response.status !== 200) {
                debugLog("cacheStore", "skipping cache for non-200 response", {
                    status: response.status,
                    pathname: ctx.pathname,
                });
                return response;
            }
            if (hasNullComponents) {
                // Proactive caching: render all segments fresh in background
                // This ensures cache has complete components for future requests
                requestCtx.waitUntil(async () => {
                    debugLog("cacheStore", "proactive caching started", {
                        pathname: ctx.pathname,
                    });
                    try {
                        // Create fresh context for proactive caching
                        // This prevents handle data from polluting the response stream
                        const proactiveHandlerContext = createHandlerContext(ctx.matched.params, ctx.request, ctx.url.searchParams, ctx.pathname, ctx.url, ctx.bindings, ctx.routeMap, ctx.matched.routeKey);
                        const proactiveLoaderPromises = new Map();
                        // Set up loader access that ignores handle pushes
                        setupLoaderAccessSilent(proactiveHandlerContext, proactiveLoaderPromises);
                        // Re-resolve ALL segments without revalidation
                        const Store = ctx.Store;
                        const freshSegments = await Store.run(() => resolveAllSegments(ctx.entries, ctx.routeKey, ctx.matched.params, proactiveHandlerContext, proactiveLoaderPromises));
                        // Also resolve intercept segments fresh if applicable
                        let freshInterceptSegments = [];
                        if (ctx.interceptResult) {
                            freshInterceptSegments = await Store.run(() => resolveInterceptEntry(ctx.interceptResult.intercept, ctx.interceptResult.entry, ctx.matched.params, proactiveHandlerContext, true));
                        }
                        const completeSegments = [
                            ...freshSegments,
                            ...freshInterceptSegments,
                        ];
                        await cacheScope.cacheRoute(ctx.pathname, ctx.matched.params, completeSegments, ctx.isIntercept);
                        debugLog("cacheStore", "proactive caching complete", {
                            pathname: ctx.pathname,
                        });
                    }
                    catch (error) {
                        debugWarn("cacheStore", "proactive caching failed", {
                            pathname: ctx.pathname,
                            error: String(error),
                        });
                    }
                });
            }
            else {
                // All segments have components - cache directly
                // Schedule caching in waitUntil since cacheRoute is now async (key resolution)
                requestCtx.waitUntil(async () => {
                    await cacheScope.cacheRoute(ctx.pathname, ctx.matched.params, allSegmentsToCache, ctx.isIntercept);
                });
            }
            return response;
        });
        if (ms) {
            ms.metrics.push({ label: "pipeline:cache-store", duration: performance.now() - pipelineStart, startTime: pipelineStart - ms.requestStart });
        }
    };
}
//# sourceMappingURL=cache-store.js.map