/**
 * Detection Stage — Runs Layer 1 + Layer 2 detectors and handles localhost aggregation.
 *
 * Returns raw findings before noisy aggregation/enrichment (those are called
 * by the orchestrator to keep stage boundaries clean).
 */

import type {
  ScanFile,
  Vulnerability,
  CancellationToken,
  DetectorContext,
  ProgressCallback,
} from '../shared/types'
import { runLayer1Scan, type Layer1Result } from './secrets'
import { runLayer2Scan, type Layer2Result } from './structural'
import { aggregateLocalhostFindings } from './config/urls'
import type { MiddlewareAuthConfig } from '../model/middleware-detector'
import type { FileAuthImports } from '../model/imported-auth-detector'
import type { FilterPipeline } from '../postprocess/filtering/pipeline'

export interface DetectorInput {
  files: ScanFile[]
  middlewareConfig?: MiddlewareAuthConfig
  fileAuthImports?: Map<string, FileAuthImports>
  detectorContext: DetectorContext
  onProgress?: ProgressCallback
  cancellationToken?: CancellationToken
  filterPipeline: FilterPipeline
  repoName: string
  quiet: boolean
}

export interface DetectorOutput {
  /** Combined Layer 1 + Layer 2 findings (after localhost aggregation) */
  findings: Vulnerability[]
  /** Phase timing */
  phaseTiming: { layer1?: number; layer2?: number }
}

const fid = (v: Pick<Vulnerability, 'filePath' | 'lineNumber' | 'category'>) =>
  `${v.filePath}:${v.lineNumber}:${v.category}`

/**
 * Run all detectors (Layer 1 + Layer 2) and return combined raw findings.
 */
export async function runDetectors(input: DetectorInput): Promise<DetectorOutput> {
  const {
    files,
    middlewareConfig,
    fileAuthImports,
    detectorContext,
    onProgress,
    cancellationToken,
    filterPipeline,
    repoName,
    quiet,
  } = input

  const log = (message: string) => {
    if (!quiet) console.log(message)
  }

  const reportProgress = (
    status: 'layer1' | 'layer2',
    message: string,
    vulnerabilitiesFound: number = 0
  ) => {
    if (onProgress) {
      onProgress({
        status,
        message,
        filesProcessed: files.length,
        totalFiles: files.length,
        vulnerabilitiesFound,
      })
    }
  }

  const phaseTiming: { layer1?: number; layer2?: number } = {}

  // Layer 1: Surface Scan
  const layer1Start = Date.now()
  reportProgress('layer1', 'Running surface scan (patterns, entropy, config)...')
  let layer1Result = await runLayer1Scan(files, onProgress, cancellationToken)

  // Aggregate repeated localhost findings
  const layer1RawCount = layer1Result.vulnerabilities.length
  const layer1BeforeAggregation = layer1Result.vulnerabilities
  layer1Result = {
    ...layer1Result,
    vulnerabilities: aggregateLocalhostFindings(layer1Result.vulnerabilities)
  }
  if (filterPipeline.isEnabled) {
    const afterIds = new Set(layer1Result.vulnerabilities.map(fid))
    for (const v of layer1BeforeAggregation) {
      if (!afterIds.has(fid(v))) {
        filterPipeline.record(fid(v), { stage: 'localhost_aggregation', action: 'aggregated', reason: 'Aggregated repeated localhost finding' })
      }
    }
  }
  phaseTiming.layer1 = Date.now() - layer1Start
  log(`[Layer1] repo=${repoName} findings_raw=${layer1RawCount} findings_deduped=${layer1Result.vulnerabilities.length} duration=${phaseTiming.layer1}ms`)

  // Layer 2: Structural Scan
  const layer2Start = Date.now()
  reportProgress('layer2', 'Running structural scan (variables, logic gates)...', layer1Result.vulnerabilities.length)
  const layer2Result = await runLayer2Scan(
    files,
    { middlewareConfig, fileAuthImports, detectorContext },
    onProgress,
    cancellationToken
  )

  // Format heuristic breakdown for logging
  const heuristicBreakdown = Object.entries(layer2Result.stats.raw)
    .filter(([, count]) => count > 0)
    .map(([name, count]) => `${name}:${count}`)
    .join(',')
  phaseTiming.layer2 = Date.now() - layer2Start
  log(`[Layer2] repo=${repoName} findings_raw=${Object.values(layer2Result.stats.raw).reduce((a, b) => a + b, 0)} findings_deduped=${layer2Result.vulnerabilities.length} duration=${phaseTiming.layer2}ms heuristic_breakdown={${heuristicBreakdown}}`)

  // Combine Layer 1 and Layer 2 findings
  const findings = [...layer1Result.vulnerabilities, ...layer2Result.vulnerabilities]

  return { findings, phaseTiming }
}

// Re-export layer results for external consumers
export { runLayer1Scan, type Layer1Result } from './secrets'
export { runLayer2Scan, type Layer2Result } from './structural'
