/**
 * Layer 1: URL Pattern Matching
 * Detects hardcoded sensitive URLs that may indicate security issues
 */

import type { Vulnerability } from '../../shared/types'
import type { ParsedFile } from '../../shared/parsed-file'
import { isTestConfigFile, isPythonFile, isInsidePythonDocstring } from '../../parse/file-classifier'
import {
  getEnvironmentContext,
  isInPlaceholderAttribute,
  isDefaultParameterValue,
  getLocalhostSeverity,
} from '../../shared/environment-context'
import { getRouteProtectionContext } from '../../model/route-hierarchy'

const BASE_CONFIDENCE_WEBHOOK = 0.55
const BASE_CONFIDENCE_LOCALHOST = 0.30

// Check if file is documentation/README/example
function isDocumentationFile(filePath: string): boolean {
  const docPatterns = [
    /README/i,
    /CHANGELOG/i,
    /CONTRIBUTING/i,
    /LICENSE/i,
    /\.md$/i,
    /\.mdx$/i,
    /\.rst$/i,
    /\.adoc$/i,
    /\/docs\//i,
    /\/documentation\//i,
    /\/wiki\//i,
    /\/guides?\//i,
    /\/tutorials?\//i,
    /\/examples?\//i,
  ]
  return docPatterns.some(p => p.test(filePath))
}

/**
 * Check if file path indicates it's inside an authenticated route group
 * Modern frameworks use route groups/layouts that handle auth at the layout level
 *
 * Examples:
 * - Next.js: (authenticated)/dashboard/page.tsx, (admin)/users/page.tsx
 * - Remix: _authenticated+/dashboard.tsx
 * - SvelteKit: (protected)/+page.svelte
 */
function isInsideAuthenticatedRouteGroup(filePath: string): boolean {
  const authRoutePatterns = [
    // Next.js App Router - route groups with auth-related names
    /\/\(authenticated\)\//i,
    /\/\(protected\)\//i,
    /\/\(auth\)\//i,
    /\/\(admin\)\//i,
    /\/\(dashboard\)\//i,
    /\/\(private\)\//i,
    /\/\(secure\)\//i,
    /\/\(user\)\//i,
    /\/\(account\)\//i,
    /\/\(settings\)\//i,
    /\/\(app\)\//i,           // Common pattern for authenticated app routes

    // Remix - authenticated route conventions
    /\/_authenticated\+\//i,
    /\/_protected\+\//i,
    /\/_auth\+\//i,
    /\/__authenticated\//i,

    // Generic patterns that indicate auth-protected sections
    /\/dashboard\//i,
    /\/admin\//i,
    /\/account\//i,
    /\/settings\//i,
    /\/profile\//i,
    /\/my-/i,                 // /my-account/, /my-orders/
    /\/user\//i,
    /\/users\/\[/i,           // /users/[userId]/ - user-specific routes
  ]

  return authRoutePatterns.some(p => p.test(filePath))
}

// Check if file is a config/constants file where localhost is expected
function isDevConfigFile(filePath: string): boolean {
  const devConfigPatterns = [
    /\.env\.local$/i,
    /\.env\.development$/i,
    /\.env\.dev$/i,
    /\.env\.example$/i,
    /\.env\.sample$/i,
    /config\.dev\./i,
    /config\.development\./i,
    /config\.local\./i,
    /\/config\/development\//i,
    /\/config\/local\//i,
    /constants\.dev\./i,
  ]
  return devConfigPatterns.some(p => p.test(filePath))
}

// Check if file is a locale/i18n file where URLs are example placeholders
function isLocaleFile(filePath: string): boolean {
  const localePatterns = [
    /\/locales?\//i,           // /locale/ or /locales/
    /\/i18n\//i,               // /i18n/
    /\/translations?\//i,      // /translation/ or /translations/
    /\/lang\//i,               // /lang/
    /\/languages?\//i,         // /language/ or /languages/
    /\.\w{2}(-\w{2})?\.json$/i, // .en.json, .zh-CN.json, etc.
  ]
  return localePatterns.some(p => p.test(filePath))
}

// Check if line contains URL placeholder/example text
function isUrlPlaceholderText(line: string): boolean {
  const placeholderPatterns = [
    /placeholder['"]\s*:/i,      // "placeholder": "http://..."
    /example['"]\s*:/i,          // "example": "http://..."
    /e\.g\./i,                   // e.g. http://localhost
    /for\s+example/i,            // for example http://...
    /such\s+as/i,                // such as http://localhost
    /like\s+http/i,              // like http://localhost
    /格式.*http/i,               // Chinese: format http://...
    /例如.*http/i,               // Chinese: for example http://...
    /beispiel.*http/i,           // German: example http://...
    /ejemplo.*http/i,            // Spanish: example http://...
    /exemple.*http/i,            // French: example http://...
  ]
  return placeholderPatterns.some(p => p.test(line))
}

// URL patterns that may indicate security issues
const URL_PATTERNS = [
  // Internal/staging endpoints in production code
  {
    pattern: /https?:\/\/localhost[:\d]*/gi,
    name: 'Localhost URL',
    severity: 'medium' as const,
    description: 'Hardcoded localhost URL found - may cause issues in production',
  },
  {
    pattern: /https?:\/\/127\.0\.0\.1[:\d]*/gi,
    name: 'Loopback URL',
    severity: 'medium' as const,
    description: 'Hardcoded loopback IP address found',
  },
  {
    pattern: /https?:\/\/[^\/]*staging[^\/]*\.[a-z]+/gi,
    name: 'Staging URL',
    severity: 'medium' as const,
    description: 'Hardcoded staging environment URL found',
  },
  {
    pattern: /https?:\/\/[^\/]*\bdev\b[^\/]*\.[a-z]+/gi,
    name: 'Development URL',
    severity: 'low' as const,
    description: 'Hardcoded development environment URL found',
  },
  {
    pattern: /https?:\/\/[^\/]*internal[^\/]*\.[a-z]+/gi,
    name: 'Internal URL',
    severity: 'high' as const,
    description: 'Hardcoded internal URL found - may expose internal infrastructure',
  },
  {
    pattern: /https?:\/\/[^\/]*\btest\b[^\/]*\.[a-z]+/gi,
    name: 'Test Environment URL',
    severity: 'low' as const,
    description: 'Hardcoded test environment URL found',
  },

  // Admin/sensitive endpoints - heavily downgraded
  // In most modern frameworks, these are protected by middleware or layout auth
  // Flagging them creates noise when they're inside authenticated route groups
  {
    pattern: /['"`]\/admin(?:\/|['"`])/gi,
    name: 'Admin Endpoint',
    severity: 'info' as const,
    description: 'Admin endpoint path found - typically protected by auth middleware',
  },
  {
    pattern: /['"`]\/api\/admin/gi,
    name: 'Admin API Endpoint',
    severity: 'info' as const,  // Downgraded from low - usually behind middleware
    description: 'Admin API endpoint found - typically protected by auth middleware',
  },

  // API keys in URLs
  {
    pattern: /\?api[_-]?key=[a-zA-Z0-9_-]{10,}/gi,
    name: 'API Key in URL Query',
    severity: 'high' as const,
    description: 'API key exposed in URL query parameter',
  },
  {
    pattern: /&api[_-]?key=[a-zA-Z0-9_-]{10,}/gi,
    name: 'API Key in URL Query',
    severity: 'high' as const,
    description: 'API key exposed in URL query parameter',
  },
  {
    pattern: /\?token=[a-zA-Z0-9_-]{10,}/gi,
    name: 'Token in URL Query',
    severity: 'high' as const,
    description: 'Token exposed in URL query parameter',
  },
  {
    pattern: /\?secret=[a-zA-Z0-9_-]{10,}/gi,
    name: 'Secret in URL Query',
    severity: 'critical' as const,
    description: 'Secret exposed in URL query parameter',
  },

  // Webhook URLs (may contain secrets)
  {
    pattern: /https:\/\/hooks\.slack\.com\/services\/[a-zA-Z0-9\/]+/gi,
    name: 'Slack Webhook URL',
    severity: 'high' as const,
    description: 'Slack webhook URL found - should be stored as environment variable',
  },
  {
    pattern: /https:\/\/discord(?:app)?\.com\/api\/webhooks\/[0-9]+\/[a-zA-Z0-9_-]+/gi,
    name: 'Discord Webhook URL',
    severity: 'high' as const,
    description: 'Discord webhook URL found - should be stored as environment variable',
  },

  // Debug/test endpoints - downgraded (often in test files or intentional)
  {
    pattern: /['"`]\/debug(?:\/|['"`])/gi,
    name: 'Debug Endpoint',
    severity: 'low' as const,  // Downgraded from medium
    description: 'Debug endpoint found - verify not accessible in production',
  },
  {
    pattern: /['"`]\/test(?:\/|['"`])/gi,
    name: 'Test Endpoint',
    severity: 'info' as const,  // Downgraded from low
    description: 'Test endpoint found - typically safe in test context',
  },
]

// Check if line is a comment
function isComment(lineContent: string): boolean {
  const trimmed = lineContent.trim()
  return (
    trimmed.startsWith('//') ||
    trimmed.startsWith('#') ||
    trimmed.startsWith('*') ||
    trimmed.startsWith('/*')
  )
}

// Check if it's in a test file
function isTestFile(filePath: string): boolean {
  return /\.(test|spec)\.(ts|tsx|js|jsx)$/i.test(filePath) ||
         /\/__tests__\//i.test(filePath) ||
         /\/test\//i.test(filePath)
}

// Check if URL is in an environment variable reference
function isEnvVarReference(lineContent: string): boolean {
  return lineContent.includes('process.env') ||
         lineContent.includes('${') ||
         lineContent.includes('import.meta.env')
}

/**
 * Check if localhost URL is used as a fallback default value
 * This is a common safe pattern in development:
 * - process.env.API_URL || 'http://localhost:3000'
 * - baseUrl ?? 'http://localhost:3000'
 * - config.url || 'http://localhost'
 * - config?.url ?? 'http://localhost'
 */
function isLocalhostFallback(lineContent: string): boolean {
  const fallbackPatterns = [
    // Logical OR fallback: something || 'http://localhost...'
    /\|\|\s*['"`]https?:\/\/(localhost|127\.0\.0\.1)/i,
    // Nullish coalescing fallback: something ?? 'http://localhost...'
    /\?\?\s*['"`]https?:\/\/(localhost|127\.0\.0\.1)/i,
    // Default parameter: = 'http://localhost...'
    /=\s*['"`]https?:\/\/(localhost|127\.0\.0\.1)[^'"]*['"`]\s*(,|\)|$)/i,
    // Ternary with env check: process.env.X ? ... : 'http://localhost'
    /process\.env\.\w+\s*\?\s*[^:]+\s*:\s*['"`]https?:\/\/(localhost|127\.0\.0\.1)/i,
    // Object property default: url: process.env.X || 'http://localhost'
    /:\s*process\.env\.\w+\s*\|\|\s*['"`]https?:\/\/(localhost|127\.0\.0\.1)/i,
    // Import.meta.env fallback
    /import\.meta\.env\.\w+\s*\|\|\s*['"`]https?:\/\/(localhost|127\.0\.0\.1)/i,
    /import\.meta\.env\.\w+\s*\?\?\s*['"`]https?:\/\/(localhost|127\.0\.0\.1)/i,
  ]
  return fallbackPatterns.some(p => p.test(lineContent))
}

// Get URL context for smarter detection
function getURLContext(lineContent: string, filePath: string): {
  isDevOnly: boolean
  isConfigFile: boolean
  isTestFile: boolean
  isEnvRef: boolean
} {
  return {
    isDevOnly: /['"]?BASE_URL['"]?|['"]?API_URL['"]?|['"]?NEXT_PUBLIC_/.test(lineContent),
    isConfigFile: /config|settings|constants/.test(filePath.toLowerCase()),
    isTestFile: isTestFile(filePath),
    isEnvRef: isEnvVarReference(lineContent)
  }
}

// Aggregate repeated localhost findings per file
export function aggregateLocalhostFindings(
  vulnerabilities: Vulnerability[]
): Vulnerability[] {
  const localhostByFile = new Map<string, {
    lines: number[]
    urls: string[]
    original: Vulnerability
  }>()

  const result: Vulnerability[] = []

  for (const vuln of vulnerabilities) {
    // Check if this is a localhost/127.0.0.1 URL
    if (vuln.category === 'sensitive_url' &&
        /localhost|127\.0\.0\.1/i.test(vuln.lineContent)) {

      const key = vuln.filePath
      if (!localhostByFile.has(key)) {
        localhostByFile.set(key, { lines: [], urls: [], original: vuln })
      }
      const entry = localhostByFile.get(key)!
      entry.lines.push(vuln.lineNumber)
      entry.urls.push(vuln.lineContent)
    } else {
      result.push(vuln)
    }
  }

  // Create aggregated findings for localhost URLs
  for (const [filePath, data] of localhostByFile) {
    const aggregated: Vulnerability = {
      ...data.original,
      title: `Localhost URLs in development code (${data.lines.length} instances)`,
      description: `Found ${data.lines.length} localhost references on lines ${data.lines.join(', ')}. ` +
        `This is typically safe in development but should use environment variables.`,
      lineNumber: data.lines[0],
      severity: 'info',  // Downgraded to info
    }
    result.push(aggregated)
  }

  return result
}

export function detectSensitiveURLs(
  content: string,
  filePath: string,
  options?: { parsed?: ParsedFile }
): Vulnerability[] {
  const vulnerabilities: Vulnerability[] = []

  // Skip documentation files entirely - they often contain example URLs
  if (isDocumentationFile(filePath)) {
    return vulnerabilities
  }

  // Get environment context for smart handling
  const envContext = getEnvironmentContext(filePath)

  // Get route protection context
  const routeHierarchy = getRouteProtectionContext(filePath)

  const lines = options?.parsed?.lines ?? content.split('\n')
  const inTestFile = isTestFile(filePath)
  const inDevConfig = isDevConfigFile(filePath)
  const inTestConfig = isTestConfigFile(filePath)
  const isPython = isPythonFile(filePath)
  const inAuthRoute = isInsideAuthenticatedRouteGroup(filePath) || routeHierarchy.isInProtectedHierarchy

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

    // Skip comments
    if (isComment(line)) continue

    // Skip Python docstrings - they often contain example URLs
    if (isPython && isInsidePythonDocstring(lines, i)) {
      continue
    }

    // Get context for this line
    const context = getURLContext(line, filePath)

    // Skip environment variable references entirely
    if (context.isEnvRef) continue

    for (const { pattern, name, severity, description } of URL_PATTERNS) {
      // Reset regex state
      const regex = new RegExp(pattern.source, pattern.flags)

      const match = regex.exec(line)
      if (match) {
        const url = match[0]

        // Special handling for localhost URLs
        if (/localhost|127\.0\.0\.1/i.test(url)) {
          // Skip localhost as fallback/default values - this is standard practice
          // e.g., process.env.API_URL || 'http://localhost:3000'
          if (isLocalhostFallback(line)) {
            continue
          }

          // Use environment context for smarter handling
          // Skip localhost in environments where it's expected (templates, tests, tooling)
          if (envContext.allowsLocalhostDefaults) {
            continue
          }

          // Skip localhost in test files, dev-only contexts, test config files, or dev config files
          if (context.isTestFile || context.isDevOnly || inDevConfig || inTestConfig) {
            continue
          }

          // Skip localhost in placeholder attributes (placeholder="https://localhost...")
          if (isInPlaceholderAttribute(line)) {
            continue
          }

          // Skip localhost when used as default parameter value
          if (isDefaultParameterValue(line)) {
            continue
          }

          // Skip localhost in locale/i18n files - these are example placeholders for users
          if (isLocaleFile(filePath)) {
            continue
          }

          // Skip localhost in placeholder/example text (e.g., "e.g. http://localhost")
          if (isUrlPlaceholderText(line)) {
            continue
          }

          // Use smart severity determination based on context
          const adjustedSeverity = getLocalhostSeverity(filePath, line)

          // Skip info-level findings in non-production contexts
          if (adjustedSeverity === 'info' && !filePath.includes('.env.production')) {
            continue
          }

          vulnerabilities.push({
            id: `url-${filePath}-${i + 1}-${name}`,
            filePath,
            lineNumber: i + 1,
            lineContent: line.trim(),
            severity: adjustedSeverity,
            category: 'sensitive_url',
            title: name,
            description: description + (adjustedSeverity === 'high' ? ' (in production config!)' : ' (in dev/config file)'),
            suggestedFix: 'Move URLs to environment variables or configuration files. Use process.env.API_URL pattern.',
            confidence: adjustedSeverity === 'high' ? 'high' : 'low',
            baseConfidence: BASE_CONFIDENCE_LOCALHOST,
            layer: 1,
        source: 'config' as const,
          })
        } else {
          // Normal URL handling (non-localhost)
          // Lower severity for test files - downgrade more aggressively
          let adjustedSeverity = severity
          let contextNote = ''

          if (inTestFile) {
            if (severity === 'critical') adjustedSeverity = 'high'
            else if (severity === 'high') adjustedSeverity = 'low'
            else adjustedSeverity = 'info'
            contextNote = ' (in test file)'
          }

          // Downgrade admin/sensitive endpoint findings inside authenticated route groups
          // These routes are already protected by layout/middleware auth
          if (inAuthRoute && (name === 'Admin Endpoint' || name === 'Admin API Endpoint')) {
            adjustedSeverity = 'info'
            contextNote = ' (inside authenticated route group)'
          }

          // Non-critical URL findings require AI validation
          const requiresAIValidation = severity !== 'critical'

          vulnerabilities.push({
            id: `url-${filePath}-${i + 1}-${name}`,
            filePath,
            lineNumber: i + 1,
            lineContent: line.trim(),
            severity: adjustedSeverity,
            category: 'sensitive_url',
            title: name,
            description: description + contextNote,
            suggestedFix: 'Move URLs to environment variables or configuration files. Use process.env.API_URL pattern.',
            confidence: inTestFile ? 'low' : 'medium',
            baseConfidence: BASE_CONFIDENCE_WEBHOOK,
            layer: 1,
        source: 'config' as const,
            requiresAIValidation,
          })
        }
        break // Only report one URL issue per line
      }
    }
  }

  return vulnerabilities
}
