/**
 * Python Route Extractor (Flask + Django)
 *
 * Detects route definitions from Flask decorators and Django URL patterns.
 */

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

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

const PUBLIC_ENDPOINT_PATTERN = /^\/(health|healthz|ready|readyz|ping|status)\/?$/i

const AUTH_DECORATOR_PATTERN = /login_required|auth_required|requires_auth|permission_required|jwt_required|token_required/i

const RATE_LIMIT_PATTERN = /rate_limit|ratelimit|throttle|limiter/i

const PYTHON_INPUT_PATTERNS: Array<{ pattern: RegExp; source: RouteInput['source'] }> = [
  { pattern: /request\.json|request\.get_json|request\.data|request\.form|request\.POST|request\.body/i, source: 'body' },
  { pattern: /request\.args|request\.GET|request\.query_params/i, source: 'query' },
  { pattern: /request\.view_args|kwargs\[|self\.kwargs/i, source: 'params' },
  { pattern: /request\.headers|request\.META/i, source: 'headers' },
  { pattern: /request\.cookies|request\.COOKIES/i, source: 'cookies' },
  { pattern: /request\.files|request\.FILES/i, source: 'files' },
]

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

/**
 * Extract Flask/Django routes from Python file content.
 *
 * Detects:
 * - Flask: @app.route('/path', methods=['GET'])
 * - Flask: @blueprint.route(...)
 * - Django: path('api/search/', views.search_view) in urlpatterns
 */
export function extractPythonRoutes(content: string, filePath: string): RouteDefinition[] {
  const routes: RouteDefinition[] = []
  const ext = filePath.substring(filePath.lastIndexOf('.'))
  if (ext !== '.py') return routes

  const lines = content.split('\n')

  // Detect Flask routes
  const flaskRoutes = extractFlaskRoutes(lines, filePath)
  routes.push(...flaskRoutes)

  // Detect Django URL patterns
  const djangoRoutes = extractDjangoRoutes(lines, filePath, content)
  routes.push(...djangoRoutes)

  return routes
}

// ============================================================================
// Flask Extraction
// ============================================================================

function extractFlaskRoutes(lines: string[], filePath: string): RouteDefinition[] {
  const routes: RouteDefinition[] = []

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

    // @app.route('/path', methods=['GET', 'POST'])
    // @blueprint.route('/path')
    const routeMatch = line.match(
      /@\s*(?:\w+)\s*\.route\s*\(\s*['"]([^'"]+)['"](?:\s*,\s*methods\s*=\s*\[([^\]]*)\])?\s*\)/
    )

    if (routeMatch) {
      const path = routeMatch[1]
      const methodsStr = routeMatch[2]
      const methods = methodsStr
        ? methodsStr.replace(/['"]/g, '').split(',').map(m => m.trim().toUpperCase()).filter(Boolean)
        : ['GET']

      // Scan decorators above the route for auth/rate-limiting
      const decorators = collectDecorators(lines, i)
      const authDecorators = decorators.filter(d => AUTH_DECORATOR_PATTERN.test(d))
      const hasRateLimit = decorators.some(d => RATE_LIMIT_PATTERN.test(d))

      // Find the function definition below the decorator
      const handlerInfo = findFunctionBelow(lines, i)
      const handlerBody = handlerInfo
        ? extractPythonFunctionBody(lines, handlerInfo.line - 1)
        : ''
      const inputs = detectPythonInputSources(handlerBody)

      routes.push({
        filePath,
        methods,
        path: normalizeFlaskPath(path),
        handler: handlerInfo?.name || 'unknown',
        handlerLine: handlerInfo?.line || i + 1,
        framework: 'flask',
        authMiddleware: authDecorators,
        rateLimiting: hasRateLimit,
        isPublic: PUBLIC_ENDPOINT_PATTERN.test(path),
        inputs,
      })
    }
  }

  return routes
}

// ============================================================================
// Django Extraction
// ============================================================================

function extractDjangoRoutes(lines: string[], filePath: string, fullContent: string): RouteDefinition[] {
  const routes: RouteDefinition[] = []

  // Only look in files that contain urlpatterns
  if (!fullContent.includes('urlpatterns')) return routes

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

    // path('api/search/', views.search_view, name='search')
    // path('api/users/<int:pk>/', UserViewSet.as_view())
    const pathMatch = line.match(
      /path\s*\(\s*['"]([^'"]*)['"]\s*,\s*([^,)]+)/
    )

    if (pathMatch) {
      const routePath = '/' + pathMatch[1].replace(/^\//, '')
      const viewRef = pathMatch[2].trim()

      routes.push({
        filePath,
        methods: ['ALL'], // Django views handle all methods by default
        path: normalizeDjangoPath(routePath),
        handler: viewRef.replace(/\.as_view\(\)/, ''),
        handlerLine: i + 1,
        framework: 'django',
        authMiddleware: [],
        rateLimiting: false,
        isPublic: PUBLIC_ENDPOINT_PATTERN.test(routePath),
        inputs: [],
      })
    }
  }

  return routes
}

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

function collectDecorators(lines: string[], routeDecoratorLine: number): string[] {
  const decorators: string[] = []
  // Look backwards from the route decorator for other decorators
  for (let j = routeDecoratorLine - 1; j >= Math.max(0, routeDecoratorLine - 10); j--) {
    const trimmed = lines[j].trim()
    if (trimmed.startsWith('@')) {
      decorators.push(trimmed)
    } else if (trimmed.length > 0) {
      break // Stop at first non-decorator, non-empty line
    }
  }
  // Also look at lines between route decorator and function def
  for (let j = routeDecoratorLine + 1; j < Math.min(lines.length, routeDecoratorLine + 5); j++) {
    const trimmed = lines[j].trim()
    if (trimmed.startsWith('@')) {
      decorators.push(trimmed)
    } else if (trimmed.startsWith('def ') || trimmed.startsWith('async def ')) {
      break
    }
  }
  return decorators
}

function findFunctionBelow(lines: string[], decoratorLine: number): { name: string; line: number } | null {
  for (let j = decoratorLine + 1; j < Math.min(lines.length, decoratorLine + 10); j++) {
    const match = lines[j].match(/(?:async\s+)?def\s+(\w+)\s*\(/)
    if (match) {
      return { name: match[1], line: j + 1 }
    }
  }
  return null
}

function extractPythonFunctionBody(lines: string[], defLineIndex: number): string {
  const end = Math.min(defLineIndex + 30, lines.length)
  return lines.slice(defLineIndex, end).join('\n')
}

function normalizeFlaskPath(path: string): string {
  // Convert Flask path params <type:name> or <name> to :name
  return path.replace(/<(?:\w+:)?(\w+)>/g, ':$1')
}

function normalizeDjangoPath(path: string): string {
  // Convert Django path params <int:pk> or <str:slug> to :pk / :slug
  return path.replace(/<(?:\w+:)?(\w+)>/g, ':$1')
}

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