/**
 * Layer 2: Structural Scan
 * Contextual analysis using variable heuristics, logic gate detection,
 * dangerous functions, risky imports, auth anti-patterns, framework checks,
 * and AI code fingerprinting
 */

import type { Vulnerability, ScanFile, CancellationToken, DetectorContext } from '../../shared/types'
import type { ProgressCallback } from '../../shared/types'
import type { MiddlewareAuthConfig } from '../../model/middleware-detector'
import { detectAuthHelpers, type AuthHelperContext } from '../../model/auth-helper-detector'
import type { FileAuthImports } from '../../model/imported-auth-detector'
import {
  filterFindingsByPath,
  type ExclusionConfig,
} from '../../parse/path-exclusions'
import { buildFileContext, type FileContext } from '../../parse/file-classifier'
import { detectSensitiveVariables } from './variables'
import { detectLogicGates } from './logic-gates'
import { detectDangerousFunctions } from './dangerous-functions'
import { detectRiskyImports } from './risky-imports'
import { detectAuthAntipatterns } from './auth-patterns'
import { detectFrameworkIssues } from './framework-checks'
import { detectAIFingerprints } from '../ai-code/fingerprinting'
import { detectDataExposure } from './data-exposure'
import { detectBYOKPatterns } from '../ai-code/byok-patterns'
// Story B: AI-specific detection modules
import { detectAIPromptHygiene } from '../ai-code/prompt-hygiene'
import { detectAIExecutionSinks } from '../ai-code/execution-sinks'
import { detectAIAgentTools } from '../ai-code/agent-tools'
// M5: New AI-era detectors
import { detectRAGSafetyIssues } from '../ai-code/rag-safety'
import { detectAIEndpointProtection } from '../ai-code/endpoint-protection'
import { detectAISchemaValidation } from '../ai-code/schema-validation'
// AI Detection Roadmap Phase 1
import { detectAIPackageHallucination } from '../ai-code/package-hallucination'
import { detectMCPSecurity } from '../ai-code/mcp-security'
// AI Detection Roadmap Phase 2
import { detectModelSupplyChain } from '../ai-code/model-supply-chain'
// OWASP Workstream 1
import { detectSecurityHeaders } from './security-headers'
import { detectSSRFPatterns } from './ssrf-detection'
import { detectLogInjection } from './log-injection'
import { detectXXEPatterns } from './xxe-detection'
// Tier system removed in Phase 2B — confidence scoring handles routing
import { severityRank } from '../../shared/parsed-file'
import { ParsedFile } from '../../shared/parsed-file'
import { applyContextAdjustments } from '../../postprocess/filtering/context-adjustments'

export interface Layer2Options {
  middlewareConfig?: MiddlewareAuthConfig
  authHelperContext?: AuthHelperContext
  fileAuthImports?: Map<string, FileAuthImports>
  /** Whether to exclude test files from scanning (default: true) */
  excludeTestFiles?: boolean
  /** Whether to exclude seed files from scanning (default: true) */
  excludeSeedFiles?: boolean
  /** Custom exclusion patterns (glob format) */
  customExclusions?: string[]
  /**
   * Detector context for context-aware detection (Phase 2b)
   * When provided, detectors can make smarter decisions based on:
   * - Project-level auth configuration
   * - Framework detection
   * - Per-file context (server vs client, test vs production)
   */
  detectorContext?: DetectorContext
}

export interface Layer2Stats {
  /** Raw finding counts per detector (before dedupe) */
  raw: Record<string, number>
  /** Deduped finding counts per category */
  deduped: Record<string, number>
  /** Severity distribution of deduped findings */
  bySeverity: Record<string, number>
  /** Findings suppressed by path exclusions */
  suppressedByPath: number
}

export interface Layer2Result {
  vulnerabilities: Vulnerability[]
  filesScanned: number
  duration: number
  /** Heuristic breakdown stats for noise analysis */
  stats: Layer2Stats
}

// Layer 2 detector stats type
type Layer2DetectorStats = {
  variables: number
  logicGates: number
  dangerousFunctions: number
  riskyImports: number
  authAntipatterns: number
  frameworkIssues: number
  aiFingerprints: number
  dataExposure: number
  byokPatterns: number
  promptHygiene: number
  executionSinks: number
  agentTools: number
  ragSafety: number
  endpointProtection: number
  schemaValidation: number
  // AI Detection Roadmap Phase 1
  packageHallucination: number
  mcpSecurity: number
  // AI Detection Roadmap Phase 2
  modelSupplyChain: number
  // OWASP Workstream 1
  securityHeaders: number
  ssrfDetection: number
  logInjection: number
  xxeDetection: number
}

// Process a single file through all Layer 2 detectors
function processFileLayer2(
  file: ScanFile,
  options: Layer2Options,
  authHelperContext: ReturnType<typeof detectAuthHelpers>
): { findings: Vulnerability[], stats: Layer2DetectorStats } {
  const stats: Layer2DetectorStats = {
    variables: 0,
    logicGates: 0,
    dangerousFunctions: 0,
    riskyImports: 0,
    authAntipatterns: 0,
    frameworkIssues: 0,
    aiFingerprints: 0,
    dataExposure: 0,
    byokPatterns: 0,
    promptHygiene: 0,
    executionSinks: 0,
    agentTools: 0,
    ragSafety: 0,
    endpointProtection: 0,
    schemaValidation: 0,
    // AI Detection Roadmap Phase 1
    packageHallucination: 0,
    mcpSecurity: 0,
    // AI Detection Roadmap Phase 2
    modelSupplyChain: 0,
    // OWASP Workstream 1
    securityHeaders: 0,
    ssrfDetection: 0,
    logInjection: 0,
    xxeDetection: 0,
  }

  // Build file-specific context for context-aware detection (Phase 2b)
  const fileContext = buildFileContext(file.path)

  // Create ParsedFile once for all detectors to share (avoids redundant content.split('\n'))
  const parsed = ParsedFile.from(file)

  // Check if this is a manifest file (package.json, requirements.txt, etc.)
  const isManifestFile = (filePath: string) => {
    const manifestFiles = ['package.json', 'requirements.txt', 'Pipfile', 'pyproject.toml', 'setup.py']
    return manifestFiles.some(f => filePath.endsWith(f))
  }

  // For non-code files, only run package hallucination detector on manifest files
  if (!isCodeFile(file.path)) {
    if (isManifestFile(file.path)) {
      // Run package hallucination detector on manifest files
      const packageHallucinationFindings = detectAIPackageHallucination(file.content, file.path)
      stats.packageHallucination = packageHallucinationFindings.length
      return { findings: packageHallucinationFindings, stats }
    }
    return { findings: [], stats }
  }

  // Run all detectors — parsed is passed via options for detectors that support it
  const variableFindings = detectSensitiveVariables(file.content, file.path, { parsed })
  const logicFindings = detectLogicGates(file.content, file.path, { parsed })
  const dangerousFuncFindings = detectDangerousFunctions(file.content, file.path, { parsed })
  const riskyImportFindings = detectRiskyImports(file.content, file.path, { parsed })
  const authFindings = detectAuthAntipatterns(file.content, file.path, {
    middlewareConfig: options.middlewareConfig,
    authHelpers: authHelperContext,
    fileAuthImports: options.fileAuthImports,
    parsed,
  })
  const frameworkFindings = detectFrameworkIssues(file.content, file.path, { parsed })
  const aiFindings = detectAIFingerprints(file.content, file.path, { parsed })
  const dataExposureFindings = detectDataExposure(file.content, file.path, { parsed })
  const byokFindings = detectBYOKPatterns(file.content, file.path, options.middlewareConfig, { parsed })
  const promptHygieneFindings = detectAIPromptHygiene(file.content, file.path, { parsed })
  const executionSinkFindings = detectAIExecutionSinks(file.content, file.path, { parsed })
  const agentToolFindings = detectAIAgentTools(file.content, file.path, { parsed })
  const ragSafetyFindings = detectRAGSafetyIssues(file.content, file.path, { parsed })
  const endpointProtectionFindings = detectAIEndpointProtection(file.content, file.path, {
    middlewareConfig: options.middlewareConfig,
    parsed,
  })
  const schemaValidationFindings = detectAISchemaValidation(file.content, file.path, { parsed })
  // AI Detection Roadmap Phase 1
  const packageHallucinationFindings = detectAIPackageHallucination(file.content, file.path, { parsed })
  const mcpSecurityFindings = detectMCPSecurity(file.content, file.path, { parsed })
  // AI Detection Roadmap Phase 2
  const modelSupplyChainFindings = detectModelSupplyChain(file.content, file.path, { parsed })
  // OWASP Workstream 1
  const securityHeaderFindings = detectSecurityHeaders(file.content, file.path, { parsed })
  const ssrfFindings = detectSSRFPatterns(file.content, file.path, { parsed })
  const logInjectionFindings = detectLogInjection(file.content, file.path, { parsed })
  const xxeFindings = detectXXEPatterns(file.content, file.path, { parsed })

  // Update stats
  stats.variables = variableFindings.length
  stats.logicGates = logicFindings.length
  stats.dangerousFunctions = dangerousFuncFindings.length
  stats.riskyImports = riskyImportFindings.length
  stats.authAntipatterns = authFindings.length
  stats.frameworkIssues = frameworkFindings.length
  stats.aiFingerprints = aiFindings.length
  stats.dataExposure = dataExposureFindings.length
  stats.byokPatterns = byokFindings.length
  stats.promptHygiene = promptHygieneFindings.length
  stats.executionSinks = executionSinkFindings.length
  stats.agentTools = agentToolFindings.length
  stats.ragSafety = ragSafetyFindings.length
  stats.endpointProtection = endpointProtectionFindings.length
  stats.schemaValidation = schemaValidationFindings.length
  // AI Detection Roadmap Phase 1
  stats.packageHallucination = packageHallucinationFindings.length
  stats.mcpSecurity = mcpSecurityFindings.length
  // AI Detection Roadmap Phase 2
  stats.modelSupplyChain = modelSupplyChainFindings.length
  // OWASP Workstream 1
  stats.securityHeaders = securityHeaderFindings.length
  stats.ssrfDetection = ssrfFindings.length
  stats.logInjection = logInjectionFindings.length
  stats.xxeDetection = xxeFindings.length

  // Combine all findings
  const allFindings = [
    ...variableFindings,
    ...logicFindings,
    ...dangerousFuncFindings,
    ...riskyImportFindings,
    ...authFindings,
    ...frameworkFindings,
    ...aiFindings,
    ...dataExposureFindings,
    ...byokFindings,
    ...promptHygieneFindings,
    ...executionSinkFindings,
    ...agentToolFindings,
    ...ragSafetyFindings,
    ...endpointProtectionFindings,
    ...schemaValidationFindings,
    // AI Detection Roadmap Phase 1
    ...packageHallucinationFindings,
    ...mcpSecurityFindings,
    // AI Detection Roadmap Phase 2
    ...modelSupplyChainFindings,
    // OWASP Workstream 1
    ...securityHeaderFindings,
    ...ssrfFindings,
    ...logInjectionFindings,
    ...xxeFindings,
  ]

  // Apply context-based severity adjustments (Phase 2b)
  const adjustedFindings = applyContextAdjustments(allFindings, fileContext)

  return {
    findings: adjustedFindings,
    stats,
  }
}

// applyFileContextAdjustments consolidated into filtering/context-adjustments.ts

// Parallel batch size for Layer 2 processing (larger batches for performance)
const LAYER2_PARALLEL_BATCH_SIZE = 50
// Progress update interval (report every N files for better UX)
const PROGRESS_UPDATE_INTERVAL = 10

export async function runLayer2Scan(
  files: ScanFile[],
  options: Layer2Options = {},
  onProgress?: ProgressCallback,
  cancellationToken?: CancellationToken
): Promise<Layer2Result> {
  const startTime = Date.now()
  const vulnerabilities: Vulnerability[] = []
  const stats: Layer2DetectorStats = {
    variables: 0,
    logicGates: 0,
    dangerousFunctions: 0,
    riskyImports: 0,
    authAntipatterns: 0,
    frameworkIssues: 0,
    aiFingerprints: 0,
    dataExposure: 0,
    byokPatterns: 0,
    promptHygiene: 0,
    executionSinks: 0,
    agentTools: 0,
    ragSafety: 0,
    endpointProtection: 0,
    schemaValidation: 0,
    // AI Detection Roadmap Phase 1
    packageHallucination: 0,
    mcpSecurity: 0,
    // AI Detection Roadmap Phase 2
    modelSupplyChain: 0,
    // OWASP Workstream 1
    securityHeaders: 0,
    ssrfDetection: 0,
    logInjection: 0,
    xxeDetection: 0,
  }

  // Detect auth helpers once for all files (if not already provided)
  const authHelperContext = options.authHelperContext || detectAuthHelpers(files)

  // Track progress for frequent updates
  let filesProcessed = 0
  let lastProgressUpdate = 0

  // Process files in parallel batches for better performance on large codebases
  for (let i = 0; i < files.length; i += LAYER2_PARALLEL_BATCH_SIZE) {
    // Check for cancellation before processing batch
    if (cancellationToken?.cancelled) break

    const batch = files.slice(i, i + LAYER2_PARALLEL_BATCH_SIZE)
    const results = await Promise.all(
      batch.map(file => Promise.resolve(processFileLayer2(file, options, authHelperContext)))
    )

    for (const result of results) {
      vulnerabilities.push(...result.findings)
      // Accumulate stats
      for (const [key, value] of Object.entries(result.stats)) {
        stats[key as keyof Layer2DetectorStats] += value
      }
    }

    filesProcessed = Math.min(i + LAYER2_PARALLEL_BATCH_SIZE, files.length)

    // Report progress every PROGRESS_UPDATE_INTERVAL files for better UX
    if (onProgress && (filesProcessed - lastProgressUpdate >= PROGRESS_UPDATE_INTERVAL || filesProcessed === files.length)) {
      onProgress({
        status: 'layer2',
        message: 'Running structural scan (variables, logic gates)...',
        filesProcessed,
        totalFiles: files.length,
        vulnerabilitiesFound: vulnerabilities.length,
      })
      lastProgressUpdate = filesProcessed
    }
  }

  // Deduplicate findings
  const dedupedVulnerabilities = deduplicateFindings(vulnerabilities)

  // Apply path exclusions (test files, seed files, etc.)
  // By default, exclude test and seed files unless explicitly disabled
  const excludeTestFiles = options.excludeTestFiles !== false
  const excludeSeedFiles = options.excludeSeedFiles !== false
  
  // Build exclusion config based on options
  const exclusionConfig: Partial<ExclusionConfig> = {}
  if (!excludeTestFiles) {
    exclusionConfig.testPatterns = []
  }
  if (!excludeSeedFiles) {
    exclusionConfig.seedPatterns = []
  }
  if (options.customExclusions) {
    // Add custom exclusions to all pattern types
    exclusionConfig.testPatterns = [
      ...(exclusionConfig.testPatterns || []),
      ...options.customExclusions,
    ]
  }

  const { kept: uniqueVulnerabilities, suppressed } = filterFindingsByPath(
    dedupedVulnerabilities,
    Object.keys(exclusionConfig).length > 0 ? exclusionConfig : undefined
  )

  // Track suppressed findings (debug info available in stats)

  // Build raw stats map for logging
  const rawStats: Record<string, number> = {
    sensitive_variables: stats.variables,
    logic_gates: stats.logicGates,
    dangerous_functions: stats.dangerousFunctions,
    risky_imports: stats.riskyImports,
    auth_antipatterns: stats.authAntipatterns,
    framework_issues: stats.frameworkIssues,
    ai_fingerprints: stats.aiFingerprints,
    data_exposure: stats.dataExposure,
    byok_patterns: stats.byokPatterns,
    ai_prompt_hygiene: stats.promptHygiene,
    ai_execution_sinks: stats.executionSinks,
    ai_agent_tools: stats.agentTools,
    // M5: New AI-era detectors
    ai_rag_safety: stats.ragSafety,
    ai_endpoint_protection: stats.endpointProtection,
    ai_schema_validation: stats.schemaValidation,
    // AI Detection Roadmap Phase 1
    ai_package_hallucination: stats.packageHallucination,
    ai_mcp_security: stats.mcpSecurity,
    // AI Detection Roadmap Phase 2
    model_supply_chain: stats.modelSupplyChain,
    // OWASP Workstream 1
    security_headers: stats.securityHeaders,
    ssrf_detection: stats.ssrfDetection,
    log_injection: stats.logInjection,
    xxe_detection: stats.xxeDetection,
  }

  // Compute deduped counts per category
  const dedupedStats: Record<string, number> = {}
  for (const vuln of uniqueVulnerabilities) {
    const cat = vuln.category
    dedupedStats[cat] = (dedupedStats[cat] || 0) + 1
  }

  // Compute severity distribution
  const severityStats: Record<string, number> = { critical: 0, high: 0, medium: 0, low: 0, info: 0 }
  for (const vuln of uniqueVulnerabilities) {
    severityStats[vuln.severity] = (severityStats[vuln.severity] || 0) + 1
  }

  return {
    vulnerabilities: uniqueVulnerabilities,
    filesScanned: files.filter(f => isCodeFile(f.path)).length,
    duration: Date.now() - startTime,
    stats: {
      raw: rawStats,
      deduped: dedupedStats,
      bySeverity: severityStats,
      suppressedByPath: suppressed.length,
    },
  }
}

// Check if file is a code file (not config/data)
function isCodeFile(filePath: string): boolean {
  const codeExtensions = [
    '.js', '.jsx', '.ts', '.tsx', '.mjs', '.cjs',
    '.py', '.rb', '.php', '.go', '.java', '.cs',
    '.rs', '.swift', '.kt', '.scala',
  ]

  return codeExtensions.some(ext => filePath.endsWith(ext))
}

// Remove duplicate findings on the same line and merge related findings
function deduplicateFindings(vulnerabilities: Vulnerability[]): Vulnerability[] {
  // First pass: exact deduplication by file:line:category
  const seen = new Map<string, Vulnerability>()

  for (const vuln of vulnerabilities) {
    const key = `${vuln.filePath}:${vuln.lineNumber}:${vuln.category}`
    const existing = seen.get(key)

    // Keep the higher severity finding
    if (!existing || severityRank(vuln.severity) > severityRank(existing.severity)) {
      seen.set(key, vuln)
    }
  }

  // Second pass: merge findings on adjacent lines (within 3 lines) with same category
  const dedupedList = Array.from(seen.values())
  const merged = mergeAdjacentFindings(dedupedList)

  // Third pass: subsume related categories (e.g., specific finding subsumes generic)
  return subsumeRelatedFindings(merged)
}

// Merge findings on adjacent lines with the same category
function mergeAdjacentFindings(vulnerabilities: Vulnerability[]): Vulnerability[] {
  if (vulnerabilities.length <= 1) return vulnerabilities

  // Group by file and category
  const groups = new Map<string, Vulnerability[]>()
  for (const vuln of vulnerabilities) {
    const key = `${vuln.filePath}:${vuln.category}`
    const group = groups.get(key) || []
    group.push(vuln)
    groups.set(key, group)
  }

  const result: Vulnerability[] = []

  for (const [, group] of groups) {
    if (group.length === 1) {
      result.push(group[0])
      continue
    }

    // Sort by line number
    group.sort((a, b) => a.lineNumber - b.lineNumber)

    // Merge adjacent findings (within 3 lines)
    let current = group[0]
    let mergedCount = 1

    for (let i = 1; i < group.length; i++) {
      const next = group[i]
      if (next.lineNumber - current.lineNumber <= 3) {
        // Merge: keep higher severity, note the merge
        mergedCount++
        if (severityRank(next.severity) > severityRank(current.severity)) {
          current = {
            ...next,
            title: current.title,
            description: current.description,
          }
        }
      } else {
        // Not adjacent - emit current and start new
        if (mergedCount > 1) {
          current = {
            ...current,
            title: `${current.title} (${mergedCount} occurrences)`,
          }
        }
        result.push(current)
        current = next
        mergedCount = 1
      }
    }

    // Emit last
    if (mergedCount > 1) {
      current = {
        ...current,
        title: `${current.title} (${mergedCount} occurrences)`,
      }
    }
    result.push(current)
  }

  return result
}

// Subsume related findings where a more specific finding covers a generic one
function subsumeRelatedFindings(vulnerabilities: Vulnerability[]): Vulnerability[] {
  // Define subsumption rules: specific category subsumes generic
  const subsumptionRules: Record<string, string[]> = {
    // SQL injection subsumes generic dangerous function
    sql_injection: ['dangerous_function'],
    // Command injection subsumes generic dangerous function
    command_injection: ['dangerous_function'],
    // XSS subsumes generic dangerous function
    xss: ['dangerous_function'],
    // Specific auth issues subsume generic missing auth
    missing_auth: ['auth_antipattern'],
  }

  // Group by file and line
  const byLocation = new Map<string, Vulnerability[]>()
  for (const vuln of vulnerabilities) {
    const key = `${vuln.filePath}:${vuln.lineNumber}`
    const group = byLocation.get(key) || []
    group.push(vuln)
    byLocation.set(key, group)
  }

  const result: Vulnerability[] = []

  for (const [, group] of byLocation) {
    if (group.length === 1) {
      result.push(group[0])
      continue
    }

    // Check for subsumption
    const toKeep = new Set(group)
    for (const vuln of group) {
      const subsumes = subsumptionRules[vuln.category]
      if (subsumes) {
        for (const other of group) {
          if (subsumes.includes(other.category) && other !== vuln) {
            toKeep.delete(other)
          }
        }
      }
    }

    result.push(...toKeep)
  }

  return result
}

// severityRank imported from utils/parsed-file

export { detectSensitiveVariables } from './variables'
export { detectLogicGates } from './logic-gates'
export { detectDangerousFunctions } from './dangerous-functions'
export { detectRiskyImports } from './risky-imports'
export { detectAuthAntipatterns } from './auth-patterns'
export { detectFrameworkIssues } from './framework-checks'
export { detectAIFingerprints } from '../ai-code/fingerprinting'
export { detectDataExposure } from './data-exposure'
export { detectBYOKPatterns } from '../ai-code/byok-patterns'
// Story B: AI-specific detectors
export { detectAIPromptHygiene } from '../ai-code/prompt-hygiene'
export { detectAIExecutionSinks } from '../ai-code/execution-sinks'
export { detectAIAgentTools } from '../ai-code/agent-tools'
// M5: New AI-era detectors
export { detectRAGSafetyIssues } from '../ai-code/rag-safety'
export { detectAIEndpointProtection } from '../ai-code/endpoint-protection'
export { detectAISchemaValidation } from '../ai-code/schema-validation'
// AI Detection Roadmap Phase 1
export { detectAIPackageHallucination } from '../ai-code/package-hallucination'
export { detectMCPSecurity } from '../ai-code/mcp-security'
// AI Detection Roadmap Phase 2
export { detectModelSupplyChain } from '../ai-code/model-supply-chain'
// OWASP Workstream 1
export { detectSecurityHeaders } from './security-headers'
export { detectSSRFPatterns } from './ssrf-detection'
export { detectLogInjection } from './log-injection'
export { detectXXEPatterns } from './xxe-detection'
