"use strict";
/**
 * Next.js Route Extractor
 *
 * Detects routes from Next.js App Router and Pages Router conventions.
 * Derives route paths from the filesystem structure.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.extractNextJSRoutes = extractNextJSRoutes;
const route_hierarchy_1 = require("../route-hierarchy");
// ============================================================================
// Constants
// ============================================================================
const APP_ROUTER_ROUTE_FILE = /\/app\/(.+)\/route\.(ts|js|tsx|jsx)$/;
const PAGES_API_FILE = /\/pages\/api\/(.+)\.(ts|js|tsx|jsx)$/;
const PUBLIC_ENDPOINT_PATTERN = /^\/(health|healthz|ready|readyz|ping|status|favicon\.ico|robots\.txt|sitemap\.xml|\.well-known)\/?$/i;
const DEFAULT_HANDLER_EXPORT = /export\s+default\s+(?:async\s+)?function\s*(\w*)/;
// Input source patterns for Next.js handlers
const NEXTJS_INPUT_PATTERNS = [
    { pattern: /request\.json\(\)|req\.body|await\s+request\.json/i, source: 'body' },
    { pattern: /searchParams|nextUrl\.searchParams|url\.searchParams|req\.query/i, source: 'query' },
    { pattern: /params\.|context\.params|props\.params/i, source: 'params' },
    { pattern: /request\.headers|req\.headers|headers\(\)/i, source: 'headers' },
    { pattern: /cookies\(\)|request\.cookies|req\.cookies/i, source: 'cookies' },
    { pattern: /formData\(\)|request\.formData/i, source: 'files' },
];
// ============================================================================
// Route Extraction
// ============================================================================
/**
 * Extract Next.js routes from scanned files.
 *
 * Detects App Router (export function GET/POST in route files)
 * and Pages Router (export default in pages/api files).
 */
function extractNextJSRoutes(files, frameworkPrimary) {
    // Only run for Next.js projects (or if framework not detected, try anyway)
    if (frameworkPrimary && frameworkPrimary !== 'nextjs')
        return [];
    const routes = [];
    for (const file of files) {
        // App Router route files
        const appMatch = file.path.match(APP_ROUTER_ROUTE_FILE);
        if (appMatch) {
            const routePath = deriveAppRouterPath(appMatch[1]);
            const methods = extractExportedMethods(file.content);
            const protection = (0, route_hierarchy_1.getRouteProtectionContext)(file.path);
            const inputs = detectNextJSInputSources(file.content);
            if (methods.length === 0)
                continue;
            for (const method of methods) {
                routes.push({
                    filePath: file.path,
                    methods: [method.name],
                    path: routePath,
                    handler: method.name,
                    handlerLine: method.line,
                    framework: 'nextjs_app',
                    authMiddleware: protection.isInProtectedHierarchy ? protection.protectionSource : [],
                    rateLimiting: false,
                    isPublic: PUBLIC_ENDPOINT_PATTERN.test(routePath),
                    inputs,
                });
            }
            continue;
        }
        // Pages Router API files
        const pagesMatch = file.path.match(PAGES_API_FILE);
        if (pagesMatch) {
            const routePath = derivePagesRouterPath(pagesMatch[1]);
            const handlerLine = findDefaultExportLine(file.content);
            const inputs = detectNextJSInputSources(file.content);
            const methods = detectPagesRouterMethods(file.content);
            routes.push({
                filePath: file.path,
                methods,
                path: routePath,
                handler: 'default',
                handlerLine,
                framework: 'nextjs_pages',
                authMiddleware: [],
                rateLimiting: false,
                isPublic: PUBLIC_ENDPOINT_PATTERN.test(routePath),
                inputs,
            });
        }
    }
    return routes;
}
// ============================================================================
// Path Derivation
// ============================================================================
/**
 * Derive API path from App Router filesystem path.
 * e.g., "api/users/[id]/route.ts" → "/api/users/:id"
 *
 * - Dynamic segments [id] → :id
 * - Catch-all [...slug] → :slug*
 * - Optional catch-all [[...slug]] → :slug*
 * - Route groups (dashboard) are stripped
 */
function deriveAppRouterPath(relativePath) {
    let path = '/' + relativePath
        // Strip route groups like (dashboard), (auth)
        .replace(/\([^)]+\)\//g, '')
        // Optional catch-all [[...param]]
        .replace(/\[\[\.\.\.(\w+)\]\]/g, ':$1*')
        // Catch-all [...param]
        .replace(/\[\.\.\.(\w+)\]/g, ':$1*')
        // Dynamic segments [param]
        .replace(/\[(\w+)\]/g, ':$1');
    // Clean up double slashes
    path = path.replace(/\/+/g, '/');
    // Remove trailing slash
    if (path.length > 1 && path.endsWith('/')) {
        path = path.slice(0, -1);
    }
    return path;
}
/**
 * Derive API path from Pages Router filesystem path.
 * e.g., "users/[id]" → "/api/users/:id"
 */
function derivePagesRouterPath(relativePath) {
    let path = '/api/' + relativePath
        .replace(/\[\.\.\.(\w+)\]/g, ':$1*')
        .replace(/\[(\w+)\]/g, ':$1');
    // Remove /index suffix
    path = path.replace(/\/index$/, '');
    // Clean up
    path = path.replace(/\/+/g, '/');
    if (path.length > 1 && path.endsWith('/')) {
        path = path.slice(0, -1);
    }
    return path;
}
function extractExportedMethods(content) {
    const methods = [];
    const lines = content.split('\n');
    for (let i = 0; i < lines.length; i++) {
        const match = lines[i].match(/export\s+(?:async\s+)?function\s+(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\s*\(/);
        if (match) {
            methods.push({ name: match[1], line: i + 1 });
        }
    }
    return methods;
}
function findDefaultExportLine(content) {
    const lines = content.split('\n');
    for (let i = 0; i < lines.length; i++) {
        if (DEFAULT_HANDLER_EXPORT.test(lines[i])) {
            return i + 1;
        }
    }
    return 1;
}
function detectPagesRouterMethods(content) {
    // Check for explicit method checks in the handler
    const methodChecks = content.match(/req\.method\s*===?\s*['"`](GET|POST|PUT|PATCH|DELETE)['"`]/gi);
    if (methodChecks) {
        return [...new Set(methodChecks.map(m => {
                const match = m.match(/['"`](GET|POST|PUT|PATCH|DELETE)['"`]/i);
                return match ? match[1].toUpperCase() : 'ALL';
            }))];
    }
    return ['ALL'];
}
function detectNextJSInputSources(content) {
    const inputs = [];
    const seen = new Set();
    for (const { pattern, source } of NEXTJS_INPUT_PATTERNS) {
        if (pattern.test(content) && !seen.has(source)) {
            seen.add(source);
            inputs.push({ source });
        }
    }
    return inputs;
}
//# sourceMappingURL=nextjs.js.map