import { matchPath } from "./route-tools.js";
import { getCodeIndex } from "./index-tools.js";
const HTTP_METHODS = ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"];
const PAGES_RE = /^src\/pages\//;
const EXT_RE = /\.(astro|ts|js)$/;
/** Convert src/pages/blog/[slug].astro → /blog/:slug */
export function fileToRoute(filePath) {
    let r = filePath.replace(PAGES_RE, "").replace(EXT_RE, "");
    r = r.replace(/\/index$/, "").replace(/^index$/, "");
    r = r.replace(/\[\.\.\.([^\]]+)\]/g, "*$1").replace(/\[([^\]]+)\]/g, ":$1");
    return "/" + r;
}
function extractDynamicParams(route) {
    const params = [];
    for (const seg of route.split("/")) {
        if (seg.startsWith(":"))
            params.push(seg.slice(1));
        else if (seg.startsWith("*"))
            params.push(seg.slice(1));
    }
    return params;
}
function routeSortKey(route) {
    return route.includes("*") ? 2 : route.includes(":") ? 1 : 0;
}
function stripSym(sym) {
    const { source: _, tokens: _t, ...rest } = sym;
    return rest;
}
function placeholder(file, name) {
    return { id: `${file}:${name}`, repo: "", name, kind: "function", file, start_line: 1, end_line: 1 };
}
/** Build all routes from an Astro project index. */
export function buildRouteEntries(index) {
    const routes = [];
    const warnings = [];
    const pageFiles = index.files.filter((f) => PAGES_RE.test(f.path) && EXT_RE.test(f.path));
    for (const file of pageFiles) {
        const routePath = fileToRoute(file.path);
        const isAstro = file.path.endsWith(".astro");
        const syms = index.symbols.filter((s) => s.file === file.path);
        const type = isAstro ? "page" : "endpoint";
        const methods = isAstro ? undefined
            : syms.filter((s) => HTTP_METHODS.includes(s.name)).map((s) => s.name);
        const has_getStaticPaths = syms.some((s) => s.name === "getStaticPaths");
        const hasPrerender = syms.some((s) => s.name === "prerender");
        const dynamicParams = extractDynamicParams(routePath);
        const isDynamic = dynamicParams.length > 0;
        const rendering = isDynamic && !has_getStaticPaths && !hasPrerender ? "server" : "static";
        if (isDynamic && !has_getStaticPaths && isAstro) {
            warnings.push(`Dynamic route "${routePath}" (${file.path}) is missing getStaticPaths — will be server-rendered`);
        }
        const layoutSym = syms.find((s) => s.name === "Layout" || s.name === "BaseLayout");
        routes.push({
            path: routePath, file: file.path, type, rendering,
            dynamic_params: dynamicParams, has_getStaticPaths,
            methods: methods && methods.length > 0 ? methods : undefined,
            layout: layoutSym?.name,
        });
    }
    routes.sort((a, b) => routeSortKey(a.path) - routeSortKey(b.path));
    // Detect conflicts: dynamic + rest at same prefix
    for (let i = 0; i < routes.length; i++) {
        for (let j = i + 1; j < routes.length; j++) {
            const a = routes[i], b = routes[j];
            const aDir = a.path.split("/").slice(0, -1).join("/");
            const bDir = b.path.split("/").slice(0, -1).join("/");
            if (aDir === bDir && a.path.includes(":") && b.path.includes("*")) {
                warnings.push(`Potential route conflict: "${a.path}" and "${b.path}" overlap at "${aDir || "/"}"`);
            }
        }
    }
    return { routes, warnings };
}
/** Find Astro route handlers matching a search path (for traceRoute dispatch). */
export function findAstroHandlers(index, searchPath) {
    const handlers = [];
    const { routes } = buildRouteEntries(index);
    for (const route of routes) {
        if (!matchPath(route.path, searchPath))
            continue;
        const syms = index.symbols.filter((s) => s.file === route.file);
        if (route.type === "endpoint" && route.methods) {
            for (const method of route.methods) {
                const sym = syms.find((s) => s.name === method);
                handlers.push({ symbol: sym ? stripSym(sym) : placeholder(route.file, method), file: route.file, method, framework: "astro" });
            }
        }
        else {
            const sym = syms[0];
            handlers.push({ symbol: sym ? stripSym(sym) : placeholder(route.file, "page"), file: route.file, framework: "astro" });
        }
    }
    return handlers;
}
/** The astro_route_map MCP tool handler. */
export async function astroRouteMap(args) {
    const index = await getCodeIndex(args.repo ?? "");
    if (!index)
        throw new Error("Repository not found");
    const { routes: allRoutes, warnings } = buildRouteEntries(index);
    const routes = args.include_endpoints === false ? allRoutes.filter((r) => r.type === "page") : allRoutes;
    return {
        routes,
        warnings,
        summary: {
            total_routes: routes.length,
            static_pages: routes.filter((r) => r.type === "page" && r.rendering === "static").length,
            server_pages: routes.filter((r) => r.type === "page" && r.rendering === "server").length,
            api_endpoints: routes.filter((r) => r.type === "endpoint").length,
            dynamic_routes: routes.filter((r) => r.dynamic_params.length > 0).length,
        },
        virtual_routes_disclaimer: [
            "Virtual/redirect routes defined in astro.config are not detected.",
            "Middleware rewrites are not reflected in this map.",
        ],
    };
}
//# sourceMappingURL=astro-routes.js.map