import path from 'path';
import { AfterRunner } from '../../server/after/run-with-after';
import { createWorkStore } from '../../server/async-storage/work-store';
import { FallbackMode } from '../../lib/fallback';
import { formatDynamicImportPath } from '../../lib/format-dynamic-import-path';
import { getRouteMatcher } from '../../shared/lib/router/utils/route-matcher';
import { getRouteRegex } from '../../shared/lib/router/utils/route-regex';
import { IncrementalCache } from '../../server/lib/incremental-cache';
import { interopDefault } from '../../lib/interop-default';
import { nodeFs } from '../../server/lib/node-fs-methods';
import { normalizePathname, encodeParam } from './utils';
import * as ciEnvironment from '../../server/ci-info';
import escapePathDelimiters from '../../shared/lib/router/utils/escape-path-delimiters';
/**
 * Compares two parameters to see if they're equal.
 *
 * @param a - The first parameter.
 * @param b - The second parameter.
 * @returns Whether the parameters are equal.
 */ function areParamValuesEqual(a, b) {
    // If they're equal, then we can return true.
    if (a === b) {
        return true;
    }
    // If they're both arrays, then we can return true if they have the same
    // length and all the items are the same.
    if (Array.isArray(a) && Array.isArray(b)) {
        if (a.length !== b.length) {
            return false;
        }
        return a.every((item)=>b.includes(item));
    }
    // Otherwise, they're not equal.
    return false;
}
/**
 * Filters out duplicate parameters from a list of parameters.
 *
 * @param routeParamKeys - The keys of the parameters.
 * @param routeParams - The list of parameters to filter.
 * @returns The list of unique parameters.
 */ function filterUniqueParams(routeParamKeys, routeParams) {
    const unique = [];
    for (const params of routeParams){
        let i = 0;
        for(; i < unique.length; i++){
            const item = unique[i];
            let j = 0;
            for(; j < routeParamKeys.length; j++){
                const key = routeParamKeys[j];
                // If the param is not the same, then we need to break out of the loop.
                if (!areParamValuesEqual(item[key], params[key])) {
                    break;
                }
            }
            // If we got to the end of the paramKeys array, then it means that we
            // found a duplicate. Skip it.
            if (j === routeParamKeys.length) {
                break;
            }
        }
        // If we didn't get to the end of the unique array, then it means that we
        // found a duplicate. Skip it.
        if (i < unique.length) {
            continue;
        }
        unique.push(params);
    }
    return unique;
}
/**
 * Filters out all combinations of root params from a list of parameters.
 *
 * Given the following root param ('lang'), and the following routeParams:
 *
 * ```
 * [
 *   { lang: 'en', region: 'US', slug: ['home'] },
 *   { lang: 'en', region: 'US', slug: ['about'] },
 *   { lang: 'fr', region: 'CA', slug: ['about'] },
 * ]
 * ```
 *
 * The result will be:
 *
 * ```
 * [
 *   { lang: 'en', region: 'US' },
 *   { lang: 'fr', region: 'CA' },
 * ]
 * ```
 *
 * @param rootParamKeys - The keys of the root params.
 * @param routeParams - The list of parameters to filter.
 * @returns The list of combinations of root params.
 */ function filterRootParamsCombinations(rootParamKeys, routeParams) {
    const combinations = [];
    for (const params of routeParams){
        const combination = {};
        // Collect all root params. As soon as we don't find a root param, break.
        let i = 0;
        for(; i < rootParamKeys.length; i++){
            const key = rootParamKeys[i];
            if (params[key]) {
                combination[key] = params[key];
            } else {
                break;
            }
        }
        // If we didn't find all root params, skip this combination. We only want to
        // generate combinations that have all root params.
        if (i < rootParamKeys.length) {
            continue;
        }
        combinations.push(combination);
    }
    return combinations;
}
/**
 * Validates the parameters to ensure they're accessible and have the correct
 * types.
 *
 * @param page - The page to validate.
 * @param regex - The route regex.
 * @param isRoutePPREnabled - Whether the route has partial prerendering enabled.
 * @param routeParamKeys - The keys of the parameters.
 * @param rootParamKeys - The keys of the root params.
 * @param routeParams - The list of parameters to validate.
 * @returns The list of validated parameters.
 */ function validateParams(page, regex, isRoutePPREnabled, routeParamKeys, rootParamKeys, routeParams) {
    const valid = [];
    // Validate that if there are any root params, that the user has provided at
    // least one value for them only if we're using partial prerendering.
    if (isRoutePPREnabled && rootParamKeys.length > 0) {
        if (routeParams.length === 0 || rootParamKeys.some((key)=>routeParams.some((params)=>!(key in params)))) {
            if (rootParamKeys.length === 1) {
                throw Object.defineProperty(new Error(`A required root parameter (${rootParamKeys[0]}) was not provided in generateStaticParams for ${page}, please provide at least one value.`), "__NEXT_ERROR_CODE", {
                    value: "E622",
                    enumerable: false
                });
            }
            throw Object.defineProperty(new Error(`Required root params (${rootParamKeys.join(', ')}) were not provided in generateStaticParams for ${page}, please provide at least one value for each.`), "__NEXT_ERROR_CODE", {
                value: "E621",
                enumerable: false
            });
        }
    }
    for (const params of routeParams){
        const item = {};
        for (const key of routeParamKeys){
            const { repeat, optional } = regex.groups[key];
            let paramValue = params[key];
            if (optional && params.hasOwnProperty(key) && (paramValue === null || paramValue === undefined || paramValue === false)) {
                paramValue = [];
            }
            // A parameter is missing, so the rest of the params are not accessible.
            // We only support this when the route has partial prerendering enabled.
            // This will make it so that the remaining params are marked as missing so
            // we can generate a fallback route for them.
            if (!paramValue && isRoutePPREnabled) {
                break;
            }
            // Perform validation for the parameter based on whether it's a repeat
            // parameter or not.
            if (repeat) {
                if (!Array.isArray(paramValue)) {
                    throw Object.defineProperty(new Error(`A required parameter (${key}) was not provided as an array received ${typeof paramValue} in generateStaticParams for ${page}`), "__NEXT_ERROR_CODE", {
                        value: "E618",
                        enumerable: false
                    });
                }
            } else {
                if (typeof paramValue !== 'string') {
                    throw Object.defineProperty(new Error(`A required parameter (${key}) was not provided as a string received ${typeof paramValue} in generateStaticParams for ${page}`), "__NEXT_ERROR_CODE", {
                        value: "E617",
                        enumerable: false
                    });
                }
            }
            item[key] = paramValue;
        }
        valid.push(item);
    }
    return valid;
}
/**
 * Builds the static paths for an app using `generateStaticParams`.
 *
 * @param params - The parameters for the build.
 * @returns The static paths.
 */ export async function buildAppStaticPaths({ dir, page, distDir, dynamicIO, authInterrupts, segments, isrFlushToDisk, cacheHandler, cacheLifeProfiles, requestHeaders, maxMemoryCacheSize, fetchCacheKeyPrefix, nextConfigOutput, ComponentMod, isRoutePPREnabled = false, buildId, rootParamKeys }) {
    if (segments.some((generate)=>{
        var _generate_config;
        return ((_generate_config = generate.config) == null ? void 0 : _generate_config.dynamicParams) === true;
    }) && nextConfigOutput === 'export') {
        throw Object.defineProperty(new Error('"dynamicParams: true" cannot be used with "output: export". See more info here: https://nextjs.org/docs/app/building-your-application/deploying/static-exports'), "__NEXT_ERROR_CODE", {
            value: "E393",
            enumerable: false
        });
    }
    ComponentMod.patchFetch();
    let CurCacheHandler;
    if (cacheHandler) {
        CurCacheHandler = interopDefault(await import(formatDynamicImportPath(dir, cacheHandler)).then((mod)=>mod.default || mod));
    }
    const incrementalCache = new IncrementalCache({
        fs: nodeFs,
        dev: true,
        dynamicIO,
        flushToDisk: isrFlushToDisk,
        serverDistDir: path.join(distDir, 'server'),
        fetchCacheKeyPrefix,
        maxMemoryCacheSize,
        getPrerenderManifest: ()=>({
                version: -1,
                routes: {},
                dynamicRoutes: {},
                notFoundRoutes: [],
                preview: null
            }),
        CurCacheHandler,
        requestHeaders,
        minimalMode: ciEnvironment.hasNextSupport
    });
    const regex = getRouteRegex(page);
    const routeParamKeys = Object.keys(getRouteMatcher(regex)(page) || {});
    const afterRunner = new AfterRunner();
    const store = createWorkStore({
        page,
        // We're discovering the parameters here, so we don't have any unknown
        // ones.
        fallbackRouteParams: null,
        renderOpts: {
            incrementalCache,
            cacheLifeProfiles,
            supportsDynamicResponse: true,
            isRevalidate: false,
            experimental: {
                dynamicIO,
                authInterrupts
            },
            waitUntil: afterRunner.context.waitUntil,
            onClose: afterRunner.context.onClose,
            onAfterTaskError: afterRunner.context.onTaskError
        },
        buildId
    });
    const routeParams = await ComponentMod.workAsyncStorage.run(store, async ()=>{
        async function builtRouteParams(parentsParams = [], idx = 0) {
            // If we don't have any more to process, then we're done.
            if (idx === segments.length) return parentsParams;
            const current = segments[idx];
            if (typeof current.generateStaticParams !== 'function' && idx < segments.length) {
                return builtRouteParams(parentsParams, idx + 1);
            }
            const params = [];
            if (current.generateStaticParams) {
                var _current_config;
                // fetchCache can be used to inform the fetch() defaults used inside
                // of generateStaticParams. revalidate and dynamic options don't come into
                // play within generateStaticParams.
                if (typeof ((_current_config = current.config) == null ? void 0 : _current_config.fetchCache) !== 'undefined') {
                    store.fetchCache = current.config.fetchCache;
                }
                if (parentsParams.length > 0) {
                    for (const parentParams of parentsParams){
                        const result = await current.generateStaticParams({
                            params: parentParams
                        });
                        for (const item of result){
                            params.push({
                                ...parentParams,
                                ...item
                            });
                        }
                    }
                } else {
                    const result = await current.generateStaticParams({
                        params: {}
                    });
                    params.push(...result);
                }
            }
            if (idx < segments.length) {
                return builtRouteParams(params, idx + 1);
            }
            return params;
        }
        return builtRouteParams();
    });
    let lastDynamicSegmentHadGenerateStaticParams = false;
    for (const segment of segments){
        var _segment_config;
        // Check to see if there are any missing params for segments that have
        // dynamicParams set to false.
        if (segment.param && segment.isDynamicSegment && ((_segment_config = segment.config) == null ? void 0 : _segment_config.dynamicParams) === false) {
            for (const params of routeParams){
                if (segment.param in params) continue;
                const relative = segment.filePath ? path.relative(dir, segment.filePath) : undefined;
                throw Object.defineProperty(new Error(`Segment "${relative}" exports "dynamicParams: false" but the param "${segment.param}" is missing from the generated route params.`), "__NEXT_ERROR_CODE", {
                    value: "E280",
                    enumerable: false
                });
            }
        }
        if (segment.isDynamicSegment && typeof segment.generateStaticParams !== 'function') {
            lastDynamicSegmentHadGenerateStaticParams = false;
        } else if (typeof segment.generateStaticParams === 'function') {
            lastDynamicSegmentHadGenerateStaticParams = true;
        }
    }
    // Determine if all the segments have had their parameters provided.
    const hadAllParamsGenerated = routeParamKeys.length === 0 || routeParams.length > 0 && routeParams.every((params)=>{
        for (const key of routeParamKeys){
            if (key in params) continue;
            return false;
        }
        return true;
    });
    // TODO: dynamic params should be allowed to be granular per segment but
    // we need additional information stored/leveraged in the prerender
    // manifest to allow this behavior.
    const dynamicParams = segments.every((segment)=>{
        var _segment_config;
        return ((_segment_config = segment.config) == null ? void 0 : _segment_config.dynamicParams) !== false;
    });
    const supportsRoutePreGeneration = hadAllParamsGenerated || process.env.NODE_ENV === 'production';
    const fallbackMode = dynamicParams ? supportsRoutePreGeneration ? isRoutePPREnabled ? FallbackMode.PRERENDER : FallbackMode.BLOCKING_STATIC_RENDER : undefined : FallbackMode.NOT_FOUND;
    const result = {
        fallbackMode,
        prerenderedRoutes: lastDynamicSegmentHadGenerateStaticParams ? [] : undefined
    };
    if (hadAllParamsGenerated || isRoutePPREnabled) {
        if (isRoutePPREnabled) {
            // Discover all unique combinations of the rootParams so we can generate
            // shells for each of them if they're available.
            routeParams.unshift(...filterRootParamsCombinations(rootParamKeys, routeParams));
            result.prerenderedRoutes ??= [];
            result.prerenderedRoutes.push({
                pathname: page,
                encodedPathname: page,
                fallbackRouteParams: routeParamKeys,
                fallbackMode: dynamicParams ? // perform a blocking static render.
                rootParamKeys.length > 0 ? FallbackMode.BLOCKING_STATIC_RENDER : fallbackMode : FallbackMode.NOT_FOUND,
                fallbackRootParams: rootParamKeys
            });
        }
        filterUniqueParams(routeParamKeys, validateParams(page, regex, isRoutePPREnabled, routeParamKeys, rootParamKeys, routeParams)).forEach((params)=>{
            let pathname = page;
            let encodedPathname = page;
            const fallbackRouteParams = [];
            for (const key of routeParamKeys){
                if (fallbackRouteParams.length > 0) {
                    // This is a partial route, so we should add the value to the
                    // fallbackRouteParams.
                    fallbackRouteParams.push(key);
                    continue;
                }
                let paramValue = params[key];
                if (!paramValue) {
                    if (isRoutePPREnabled) {
                        // This is a partial route, so we should add the value to the
                        // fallbackRouteParams.
                        fallbackRouteParams.push(key);
                        continue;
                    } else {
                        // This route is not complete, and we aren't performing a partial
                        // prerender, so we should return, skipping this route.
                        return;
                    }
                }
                const { repeat, optional } = regex.groups[key];
                let replaced = `[${repeat ? '...' : ''}${key}]`;
                if (optional) {
                    replaced = `[${replaced}]`;
                }
                pathname = pathname.replace(replaced, encodeParam(paramValue, (value)=>escapePathDelimiters(value, true)));
                encodedPathname = encodedPathname.replace(replaced, encodeParam(paramValue, encodeURIComponent));
            }
            const fallbackRootParams = rootParamKeys.filter((param)=>fallbackRouteParams.includes(param));
            result.prerenderedRoutes ??= [];
            result.prerenderedRoutes.push({
                pathname: normalizePathname(pathname),
                encodedPathname: normalizePathname(encodedPathname),
                fallbackRouteParams,
                fallbackMode: dynamicParams ? // perform a blocking static render.
                fallbackRootParams.length > 0 ? FallbackMode.BLOCKING_STATIC_RENDER : fallbackMode : FallbackMode.NOT_FOUND,
                fallbackRootParams
            });
        });
    }
    await afterRunner.executeAfter();
    return result;
}

//# sourceMappingURL=app.js.map