import "../require-hook";
import "../node-polyfill-fetch";
import url from "url";
import path from "path";
import { findPageFile } from "./find-page-file";
import { getRequestMeta } from "../request-meta";
import setupDebug from "next/dist/compiled/debug";
import { getCloneableBody } from "../body-streams";
import { findPagesDir } from "../../lib/find-pages-dir";
import { setupFsCheck } from "./router-utils/filesystem";
import { proxyRequest } from "./router-utils/proxy-request";
import { getResolveRoutes } from "./router-utils/resolve-routes";
import { PERMANENT_REDIRECT_STATUS } from "../../shared/lib/constants";
import { formatHostname } from "./format-hostname";
import { signalFromNodeResponse } from "../web/spec-extension/adapters/next-request";
import { getMiddlewareRouteMatcher } from "../../shared/lib/router/utils/middleware-route-matcher";
import { pipeReadable } from "../pipe-readable";
const debug = setupDebug("next:router-server");
export async function makeResolver(dir, nextConfig, middleware, { hostname ="localhost" , port =3000  }) {
    const fsChecker = await setupFsCheck({
        dir,
        dev: true,
        minimalMode: false,
        config: nextConfig
    });
    const { appDir , pagesDir  } = findPagesDir(dir);
    // we format the hostname so that it can be fetched
    const fetchHostname = formatHostname(hostname);
    fsChecker.ensureCallback(async (item)=>{
        let result = null;
        if (item.type === "appFile") {
            if (!appDir) {
                throw new Error("no app dir present");
            }
            result = await findPageFile(appDir, item.itemPath, nextConfig.pageExtensions, true);
        } else if (item.type === "pageFile") {
            if (!pagesDir) {
                throw new Error("no pages dir present");
            }
            result = await findPageFile(pagesDir, item.itemPath, nextConfig.pageExtensions, false);
        }
        if (!result) {
            throw new Error(`failed to find page file ${item.type} ${item.itemPath}`);
        }
    });
    const distDir = path.join(dir, nextConfig.distDir);
    const middlewareInfo = middleware ? {
        name: "middleware",
        paths: middleware.files.map((file)=>path.join(process.cwd(), file)),
        wasm: [],
        assets: []
    } : {};
    if (middleware == null ? void 0 : middleware.files.length) {
        var _middleware_matcher;
        fsChecker.middlewareMatcher = getMiddlewareRouteMatcher(((_middleware_matcher = middleware.matcher) == null ? void 0 : _middleware_matcher.map((item)=>({
                regexp: item,
                originalSource: item
            }))) || [
            {
                regexp: ".*",
                originalSource: "/:path*"
            }
        ]);
    }
    const resolveRoutes = getResolveRoutes(fsChecker, nextConfig, {
        dir,
        port,
        hostname,
        isNodeDebugging: false,
        dev: true,
        workerType: "render"
    }, {
        pages: {
            async initialize () {
                return {
                    async requestHandler (req, res) {
                        if (!req.headers["x-middleware-invoke"]) {
                            throw new Error(`Invariant unexpected request handler call`);
                        }
                        const cloneableBody = getCloneableBody(req);
                        const { run  } = require("../web/sandbox");
                        const result = await run({
                            distDir,
                            name: middlewareInfo.name || "/",
                            paths: middlewareInfo.paths || [],
                            edgeFunctionEntry: middlewareInfo,
                            request: {
                                headers: req.headers,
                                method: req.method || "GET",
                                nextConfig: {
                                    i18n: nextConfig.i18n,
                                    basePath: nextConfig.basePath,
                                    trailingSlash: nextConfig.trailingSlash
                                },
                                url: `http://${fetchHostname}:${port}${req.url}`,
                                body: cloneableBody,
                                signal: signalFromNodeResponse(res)
                            },
                            useCache: true,
                            onWarning: console.warn
                        });
                        const err = new Error();
                        err.result = result;
                        throw err;
                    },
                    async upgradeHandler () {
                        throw new Error(`Invariant: unexpected upgrade handler call`);
                    }
                };
            },
            deleteAppClientCache () {},
            async deleteCache () {},
            async clearModuleContext () {},
            async propagateServerField () {}
        }
    }, {});
    return async function resolveRoute(req, res) {
        const routeResult = await resolveRoutes({
            req,
            res,
            isUpgradeReq: false,
            signal: signalFromNodeResponse(res)
        });
        const { matchedOutput , bodyStream , statusCode , parsedUrl , resHeaders , finished  } = routeResult;
        debug("requestHandler!", req.url, {
            matchedOutput,
            statusCode,
            resHeaders,
            bodyStream: !!bodyStream,
            parsedUrl: {
                pathname: parsedUrl.pathname,
                query: parsedUrl.query
            },
            finished
        });
        for (const key of Object.keys(resHeaders || {})){
            res.setHeader(key, resHeaders[key]);
        }
        if (!bodyStream && statusCode && statusCode > 300 && statusCode < 400) {
            const destination = url.format(parsedUrl);
            res.statusCode = statusCode;
            res.setHeader("location", destination);
            if (statusCode === PERMANENT_REDIRECT_STATUS) {
                res.setHeader("Refresh", `0;url=${destination}`);
            }
            res.end(destination);
            return;
        }
        // handle middleware body response
        if (bodyStream) {
            res.statusCode = statusCode || 200;
            return await pipeReadable(bodyStream, res);
        }
        if (finished && parsedUrl.protocol) {
            var _getRequestMeta;
            await proxyRequest(req, res, parsedUrl, undefined, (_getRequestMeta = getRequestMeta(req, "__NEXT_CLONABLE_BODY")) == null ? void 0 : _getRequestMeta.cloneBodyStream(), nextConfig.experimental.proxyTimeout);
            return;
        }
        res.setHeader("x-nextjs-route-result", "1");
        res.end();
        return {
            type: "rewrite",
            statusCode: 200,
            headers: resHeaders,
            url: url.format(parsedUrl)
        };
    };
}

//# sourceMappingURL=route-resolver.js.map