/**
 * Intercept Resolution
 *
 * Extracted from createRouter closure. Contains intercept detection and resolution
 * functions for soft navigation (modals).
 */
import { evaluateRevalidation } from "./revalidation.js";
import { getRequestContext } from "../server/request-context.js";
import { executeInterceptMiddleware } from "./middleware.js";
import { handleHandlerResult } from "./segment-resolution.js";
import { debugLog } from "./logging.js";
/**
 * Check if an intercept's when conditions are satisfied.
 * All when() functions must return true for the intercept to activate.
 * If no when() conditions are defined, the intercept always activates.
 *
 * During action revalidation, when() is NOT evaluated.
 */
export function evaluateInterceptWhen(intercept, selectorContext, isAction) {
    if (isAction) {
        return true;
    }
    if (!intercept.when || intercept.when.length === 0) {
        return true;
    }
    if (!selectorContext) {
        return false;
    }
    return intercept.when.every((fn) => fn(selectorContext));
}
/**
 * Find an intercept for the target route by walking up the entry chain.
 * Returns the first (innermost) matching intercept along with the entry that defines it.
 */
export function findInterceptForRoute(targetRouteKey, fromEntry, selectorContext = null, isAction = false) {
    let current = fromEntry;
    while (current) {
        if (current.intercept && current.intercept.length > 0) {
            for (const intercept of current.intercept) {
                if (intercept.routeName === targetRouteKey &&
                    evaluateInterceptWhen(intercept, selectorContext, isAction)) {
                    return { intercept, entry: current };
                }
            }
        }
        if (current.layout && current.layout.length > 0) {
            for (const siblingLayout of current.layout) {
                if (siblingLayout.intercept && siblingLayout.intercept.length > 0) {
                    for (const intercept of siblingLayout.intercept) {
                        if (intercept.routeName === targetRouteKey &&
                            evaluateInterceptWhen(intercept, selectorContext, isAction)) {
                            return { intercept, entry: siblingLayout };
                        }
                    }
                }
            }
        }
        current = current.parent;
    }
    return null;
}
/**
 * Resolve an intercept entry and emit segment with the slot name.
 */
export async function resolveInterceptEntry(interceptEntry, parentEntry, params, context, belongsToRoute, deps, revalidationContext) {
    const segments = [];
    if (interceptEntry.middleware.length > 0) {
        const requestCtx = getRequestContext();
        if (!requestCtx?.res) {
            throw new Error("Request context with stubResponse is required for intercept middleware");
        }
        const middlewareResponse = await executeInterceptMiddleware(interceptEntry.middleware, context.request, context.env, params, context.var, requestCtx.res);
        if (middlewareResponse)
            throw middlewareResponse;
    }
    const loaderPromises = [];
    const loaderIds = [];
    for (let i = 0; i < interceptEntry.loader.length; i++) {
        const { loader, revalidate: loaderRevalidateFns } = interceptEntry.loader[i];
        const segmentId = `${parentEntry.shortCode}.${interceptEntry.slotName}D${i}.${loader.$$id}`;
        if (revalidationContext) {
            const { clientSegmentIds, prevParams, request, prevUrl, nextUrl, routeKey, actionContext, stale, } = revalidationContext;
            const interceptSegmentId = `${parentEntry.shortCode}.${interceptEntry.slotName}`;
            if (clientSegmentIds.has(interceptSegmentId)) {
                const dummySegment = {
                    id: segmentId,
                    namespace: `intercept:${interceptEntry.routeName}`,
                    type: "loader",
                    index: i,
                    component: null,
                    params,
                    loaderId: loader.$$id,
                    belongsToRoute,
                };
                const shouldRevalidate = await evaluateRevalidation({
                    segment: dummySegment,
                    prevParams,
                    getPrevSegment: null,
                    request,
                    prevUrl,
                    nextUrl,
                    revalidations: loaderRevalidateFns.map((fn, j) => ({
                        name: `intercept-loader-revalidate${j}`,
                        fn,
                    })),
                    routeKey,
                    context,
                    actionContext,
                    stale,
                });
                if (!shouldRevalidate) {
                    debugLog("intercept.loader", "skipped revalidation", {
                        loaderId: loader.$$id,
                    });
                    continue;
                }
                debugLog("intercept.loader", "revalidating", {
                    loaderId: loader.$$id,
                    stale,
                });
            }
        }
        loaderIds.push(loader.$$id);
        loaderPromises.push(deps.wrapLoaderPromise(context.use(loader), parentEntry, segmentId, context.pathname));
    }
    const handlerResult = typeof interceptEntry.handler === "function"
        ? handleHandlerResult(interceptEntry.handler(context))
        : interceptEntry.handler;
    let layoutElement;
    if (interceptEntry.layout) {
        if (typeof interceptEntry.layout === "function") {
            const layoutResult = await interceptEntry.layout(context);
            if (layoutResult instanceof Response) {
                throw layoutResult;
            }
            layoutElement = layoutResult;
        }
        else {
            layoutElement = interceptEntry.layout;
        }
    }
    let component;
    let loaderDataPromise;
    if (interceptEntry.loading && loaderPromises.length > 0) {
        component =
            handlerResult instanceof Promise
                ? handlerResult
                : Promise.resolve(handlerResult);
        loaderDataPromise = Promise.all(loaderPromises);
    }
    else if (loaderPromises.length > 0) {
        loaderDataPromise = await Promise.all(loaderPromises);
        component =
            handlerResult instanceof Promise ? await handlerResult : handlerResult;
    }
    else {
        component =
            interceptEntry.loading && handlerResult instanceof Promise
                ? handlerResult
                : handlerResult instanceof Promise
                    ? await handlerResult
                    : handlerResult;
    }
    const interceptSegment = {
        id: `${parentEntry.shortCode}.${interceptEntry.slotName}`,
        namespace: `intercept:${interceptEntry.routeName}`,
        type: "parallel",
        index: 0,
        component,
        loading: interceptEntry.loading === false ? null : interceptEntry.loading,
        layout: layoutElement,
        params,
        slot: interceptEntry.slotName,
        belongsToRoute,
        parallelName: `intercept:${interceptEntry.routeName}.${interceptEntry.slotName}`,
        loaderDataPromise,
        loaderIds: loaderIds.length > 0 ? loaderIds : undefined,
    };
    segments.push(interceptSegment);
    return segments;
}
/**
 * Resolve only the loaders for a cached intercept segment.
 * Used on intercept cache hit to get fresh loader data while keeping cached component/layout.
 */
export async function resolveInterceptLoadersOnly(interceptEntry, parentEntry, params, context, belongsToRoute, deps, revalidationContext) {
    if (interceptEntry.loader.length === 0) {
        return null;
    }
    const loaderPromises = [];
    const loaderIds = [];
    const { clientSegmentIds, prevParams, request, prevUrl, nextUrl, routeKey, actionContext, stale, } = revalidationContext;
    for (let i = 0; i < interceptEntry.loader.length; i++) {
        const { loader, revalidate: loaderRevalidateFns } = interceptEntry.loader[i];
        const segmentId = `${parentEntry.shortCode}.${interceptEntry.slotName}D${i}.${loader.$$id}`;
        const interceptSegmentId = `${parentEntry.shortCode}.${interceptEntry.slotName}`;
        if (clientSegmentIds.has(interceptSegmentId)) {
            const dummySegment = {
                id: segmentId,
                namespace: `intercept:${interceptEntry.routeName}`,
                type: "loader",
                index: i,
                component: null,
                params,
                loaderId: loader.$$id,
                belongsToRoute,
            };
            const shouldRevalidate = await evaluateRevalidation({
                segment: dummySegment,
                prevParams,
                getPrevSegment: null,
                request,
                prevUrl,
                nextUrl,
                revalidations: loaderRevalidateFns.map((fn, j) => ({
                    name: `intercept-loader-revalidate${j}`,
                    fn,
                })),
                routeKey,
                context,
                actionContext,
                stale,
            });
            if (!shouldRevalidate) {
                debugLog("intercept.loader", "skipped on cache hit", {
                    loaderId: loader.$$id,
                });
                continue;
            }
            debugLog("intercept.loader", "revalidating on cache hit", {
                loaderId: loader.$$id,
                stale,
            });
        }
        loaderIds.push(loader.$$id);
        loaderPromises.push(deps.wrapLoaderPromise(context.use(loader), parentEntry, segmentId, context.pathname));
    }
    if (loaderPromises.length === 0) {
        return null;
    }
    const loaderDataPromise = interceptEntry.loading !== undefined
        ? Promise.all(loaderPromises)
        : await Promise.all(loaderPromises);
    return { loaderDataPromise, loaderIds };
}
//# sourceMappingURL=intercept-resolution.js.map