/**
 * Express/Fastify/Hono/Koa Route Extractor
 *
 * Detects route definitions and mount points from Express-style frameworks.
 * Also handles Fastify (preHandler), Hono, and Koa route patterns.
 */

import type { RouteDefinition, RouteInput, MountPoint, RouteFramework } from './types'
import { extractBlock } from './utils'

// ============================================================================
// Constants
// ============================================================================

const HTTP_METHODS = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'all']

const RATE_LIMIT_PATTERN = /rateLimit|rateLimiter|throttle|slowDown/i

const PUBLIC_ENDPOINT_PATTERN = /^\/(health|healthz|ready|readyz|ping|status|favicon\.ico|robots\.txt|sitemap\.xml|\.well-known)\/?$/i

/**
 * Pattern to identify auth-enforcing middleware on routes.
 * Must be specific — "verify" alone matches too broadly (e.g., verify.challengeChecks).
 */
const AUTH_MIDDLEWARE_PATTERN = /auth|isAuthorized|isAuthenticated|isAccounting|isAdmin|isDeluxe|passport|verifyToken|verifyJwt|jwtAuth|jwtVerify|bearerAuth|clerk|denyAll/i

// Input source patterns to scan for in handler bodies
const INPUT_PATTERNS: Array<{ pattern: RegExp; source: RouteInput['source'] }> = [
  { pattern: /req\.body|request\.body|ctx\.request\.body|c\.req\.json|c\.req\.parseBody/i, source: 'body' },
  { pattern: /req\.query|request\.query|ctx\.query|c\.req\.query|searchParams/i, source: 'query' },
  { pattern: /req\.params|request\.params|ctx\.params|c\.req\.param/i, source: 'params' },
  { pattern: /req\.headers|request\.headers|ctx\.headers|c\.req\.header/i, source: 'headers' },
  { pattern: /req\.cookies|request\.cookies|ctx\.cookies/i, source: 'cookies' },
  { pattern: /req\.files?|request\.files?|multer|upload\./i, source: 'files' },
]

// ============================================================================
// Route Extraction
// ============================================================================

/**
 * Extract Express/Fastify/Hono/Koa routes from file content.
 *
 * Patterns detected:
 * - app.get('/path', handler) / router.post(...) / server.put(...)
 * - Fastify route({ method, url, preHandler, handler })
 * - Hono app.get('/path', handler)
 */
export function extractExpressRoutes(content: string, filePath: string): RouteDefinition[] {
  const routes: RouteDefinition[] = []
  const lines = content.split('\n')
  const framework = detectExpressFamily(content)

  for (let i = 0; i < lines.length; i++) {
    const line = lines[i]
    const lineNum = i + 1

    // Pattern: <identifier>.get('/path', ...middleware, handler)
    // Matches any receiver (app, router, server, custom names) calling an HTTP method
    // with a path argument starting with '/'
    const routeMatch = line.match(
      /\b\w+\s*\.\s*(get|post|put|patch|delete|head|options|all)\s*\(\s*['"`]([^'"`]+)['"`]/i
    )

    if (routeMatch && routeMatch[2].startsWith('/')) {
      const method = routeMatch[1].toUpperCase()
      const path = routeMatch[2]
      const middleware = extractMiddlewareFromLine(line)
      const handlerBody = extractHandlerBody(lines, i)
      const inputs = detectInputSources(handlerBody)

      routes.push({
        filePath,
        methods: [method],
        path,
        handler: extractHandlerName(line) || 'anonymous',
        handlerLine: lineNum,
        framework,
        authMiddleware: middleware.filter(m => AUTH_MIDDLEWARE_PATTERN.test(m)),
        rateLimiting: middleware.some(m => RATE_LIMIT_PATTERN.test(m)) ||
                      RATE_LIMIT_PATTERN.test(handlerBody),
        isPublic: PUBLIC_ENDPOINT_PATTERN.test(path),
        inputs,
      })
      continue
    }

    // Fastify route object pattern: server.route({ method: 'GET', url: '/path', ... })
    const fastifyMatch = line.match(/\.route\s*\(\s*\{/)
    if (fastifyMatch) {
      const blockText = extractBlock(lines, i)
      const methodMatch = blockText.match(/method\s*:\s*['"`](\w+)['"`]/)
      const urlMatch = blockText.match(/url\s*:\s*['"`]([^'"`]+)['"`]/)
      if (methodMatch && urlMatch) {
        const preHandlerMatch = blockText.match(/preHandler\s*:\s*\[([^\]]*)\]/)
        const preHandlerMiddleware = preHandlerMatch
          ? preHandlerMatch[1].split(',').map(m => m.trim()).filter(Boolean)
          : []
        const inputs = detectInputSources(blockText)

        routes.push({
          filePath,
          methods: [methodMatch[1].toUpperCase()],
          path: urlMatch[1],
          handler: 'route_handler',
          handlerLine: lineNum,
          framework: 'fastify',
          authMiddleware: preHandlerMiddleware.filter(m => AUTH_MIDDLEWARE_PATTERN.test(m)),
          rateLimiting: preHandlerMiddleware.some(m => RATE_LIMIT_PATTERN.test(m)),
          isPublic: PUBLIC_ENDPOINT_PATTERN.test(urlMatch[1]),
          inputs,
        })
      }
    }
  }

  return routes
}

// ============================================================================
// Mount Point Extraction
// ============================================================================

/**
 * Extract mount points (app.use) from file content.
 *
 * Patterns:
 * - app.use('/api', authMiddleware) — path-scoped mount
 * - app.use(helmet()) — global mount (no path)
 */
export function extractMountPoints(content: string, filePath: string): MountPoint[] {
  const mounts: MountPoint[] = []
  const lines = content.split('\n')

  for (let i = 0; i < lines.length; i++) {
    const line = lines[i]
    const lineNum = i + 1

    // app.use('/path', middleware) — path-scoped
    const scopedMatch = line.match(
      /(?:app|router|server)\s*\.use\s*\(\s*['"`]([^'"`]+)['"`]\s*,\s*(.+)\)/
    )
    if (scopedMatch) {
      const middleware = extractMiddlewareNames(scopedMatch[2])
      if (middleware.length > 0) {
        mounts.push({
          path: scopedMatch[1],
          middleware,
          line: lineNum,
          isGlobal: false,
          filePath,
        })
      }
      continue
    }

    // app.use(middleware) — global (no path argument)
    // Check that line has .use( but no string as first argument
    const globalUseMatch = line.match(/(?:app|router|server)\s*\.use\s*\(/)
    if (globalUseMatch && !scopedMatch) {
      // Verify no string path (already handled above)
      const afterUse = line.slice(line.indexOf('.use(') + 5)
      if (!/^\s*['"`]/.test(afterUse)) {
        // Extract middleware names from the arguments
        const argsStr = extractParenContent(line, line.indexOf('.use(') + 4)
        const middleware = extractMiddlewareNames(argsStr)
        if (middleware.length > 0) {
          mounts.push({
            path: '/',
            middleware,
            line: lineNum,
            isGlobal: true,
            filePath,
          })
        }
      }
    }
  }

  return mounts
}

// ============================================================================
// Internal Helpers
// ============================================================================

function detectExpressFamily(content: string): RouteFramework {
  if (/require\s*\(\s*['"]fastify['"]\)|from\s+['"]fastify['"]/.test(content)) return 'fastify'
  if (/require\s*\(\s*['"]hono['"]\)|from\s+['"]hono['"]/.test(content)) return 'hono'
  if (/require\s*\(\s*['"]koa['"]\)|from\s+['"]koa['"]/.test(content)) return 'koa'
  return 'express'
}

function extractMiddlewareFromLine(line: string): string[] {
  // Extract arguments between the path string and the last argument (handler)
  // e.g., app.post('/path', auth, validate, handler) → ['auth', 'validate']
  const afterPath = line.replace(/^[^(]*\(\s*['"`][^'"`]*['"`]\s*,\s*/, '')
  const args = afterPath.replace(/\)\s*;?\s*$/, '').split(',').map(a => a.trim()).filter(Boolean)
  // Remove the last argument (the handler function) if there are multiple
  if (args.length > 1) {
    return args.slice(0, -1).map(a => a.replace(/\(.*\)/, '').trim())
  }
  return []
}

function extractHandlerName(line: string): string | null {
  // Look for named function reference as last argument
  const match = line.match(/,\s*(\w+)\s*\)\s*;?\s*$/)
  if (match && !match[1].match(/^(async|function|req|res|next|ctx|c)$/)) {
    return match[1]
  }
  return null
}

function extractParenContent(line: string, openParenIndex: number): string {
  let depth = 0
  let start = openParenIndex + 1
  for (let i = openParenIndex; i < line.length; i++) {
    if (line[i] === '(') depth++
    if (line[i] === ')') {
      depth--
      if (depth === 0) {
        return line.slice(start, i)
      }
    }
  }
  return line.slice(start)
}

function extractMiddlewareNames(text: string): string[] {
  return text
    .split(',')
    .map(m => m.trim().replace(/\(.*\)/, '').trim())
    .filter(m => m.length > 0 && /^[a-zA-Z_$]/.test(m))
}

function extractHandlerBody(lines: string[], startLine: number): string {
  return extractBlock(lines, startLine)
}

function detectInputSources(body: string): RouteInput[] {
  const inputs: RouteInput[] = []
  const seen = new Set<string>()
  for (const { pattern, source } of INPUT_PATTERNS) {
    if (pattern.test(body) && !seen.has(source)) {
      seen.add(source)
      inputs.push({ source })
    }
  }
  return inputs
}
