import { generateServerTiming, logMetrics } from "./metrics.js";
import { debugLog } from "./logging.js";
/**
 * Collect all segments from an async generator
 */
export async function collectSegments(generator) {
    const segments = [];
    for await (const segment of generator) {
        segments.push(segment);
    }
    return segments;
}
/**
 * Build the final MatchResult from collected segments and context
 */
export function buildMatchResult(allSegments, ctx, state) {
    const logPrefix = ctx.isFullMatch ? "[Router.match]" : "[Router.matchPartial]";
    let allIds;
    let segmentsToRender;
    if (ctx.isFullMatch) {
        // Full match (document request) - all segments are rendered
        // Deduplicate by segment ID (defense-in-depth). The primary dedup is in
        // resolveAllSegments, but this guards against any path that bypasses it.
        // include() scopes can produce entries that resolve the same shared layout,
        // and duplicate IDs change the client's React tree depth causing remounts.
        const seen = new Set();
        segmentsToRender = [];
        for (const s of allSegments) {
            if (!seen.has(s.id)) {
                seen.add(s.id);
                segmentsToRender.push(s);
            }
        }
        allIds = segmentsToRender.map((s) => s.id);
    }
    else {
        // Partial match (navigation) - filter and handle intercepts
        // When intercepting, tell browser to keep its current segments + add modal
        // This prevents the browser from discarding the current page content
        // If client sent empty segments (HMR recovery), use segment IDs from allSegments
        allIds = ctx.interceptResult
            ? ctx.clientSegmentIds.length > 0
                ? [...ctx.clientSegmentIds, ...state.interceptSegments.map((s) => s.id)]
                : allSegments.map((s) => s.id) // Use actual segments, not matchedIds
            : [...state.matchedIds, ...state.interceptSegments.map((s) => s.id)];
        // Deduplicate allIds (defense-in-depth for partial match path)
        allIds = [...new Set(allIds)];
        // Filter out segments with null components (client already has them)
        // BUT always include loader segments - they carry data even with null component
        segmentsToRender = allSegments.filter((s) => s.component !== null || s.type === "loader");
    }
    debugLog(logPrefix, "all segments", {
        segments: allSegments.map((s) => ({
            id: s.id,
            type: s.type,
            hasComponent: s.component !== null,
        })),
    });
    debugLog(logPrefix, "segments to render", {
        segmentIds: segmentsToRender.map((s) => s.id),
    });
    // Output metrics if enabled
    let serverTiming;
    if (ctx.metricsStore) {
        logMetrics(ctx.request.method, ctx.pathname, ctx.metricsStore);
        serverTiming = generateServerTiming(ctx.metricsStore);
    }
    return {
        segments: segmentsToRender,
        matched: allIds,
        diff: segmentsToRender.map((s) => s.id),
        params: ctx.matched.params,
        routeName: ctx.routeKey,
        serverTiming,
        slots: Object.keys(state.slots).length > 0 ? state.slots : undefined,
        routeMiddleware: ctx.routeMiddleware.length > 0 ? ctx.routeMiddleware : undefined,
    };
}
/**
 * Collect segments from pipeline and build MatchResult
 *
 * This is the main entry point for building the final result after
 * the pipeline has processed all segments.
 */
export async function collectMatchResult(pipeline, ctx, state) {
    const allSegments = await collectSegments(pipeline);
    // Update state with collected segments if not already set
    if (state.segments.length === 0) {
        state.segments = allSegments;
    }
    return buildMatchResult(allSegments, ctx, state);
}
//# sourceMappingURL=match-result.js.map