/**
 * Scanner Types and Interfaces
 * Defines the core data structures for the security scanning engine
 */

export type VulnerabilitySeverity = 'critical' | 'high' | 'medium' | 'low' | 'info'

export type VulnerabilityCategory =
  | 'hardcoded_secret'
  | 'high_entropy_string'
  | 'sensitive_variable'
  | 'security_bypass'
  | 'dangerous_function'
  | 'sql_injection'
  | 'xss'
  | 'command_injection'
  | 'insecure_config'
  | 'missing_auth'
  | 'suspicious_package'
  | 'cors_misconfiguration'
  | 'root_container'
  | 'dangerous_file'
  | 'ai_pattern'
  | 'sensitive_url'
  | 'weak_crypto'
  | 'data_exposure'  // Logging/exposing sensitive data
  // AI-specific categories (Story B)
  | 'ai_prompt_injection'     // B1: Prompt hygiene issues - user input in system prompts
  | 'ai_unsafe_execution'     // B2: LLM output fed to dangerous sinks (eval, exec, SQL)
  | 'ai_overpermissive_tool'  // B4: Agent tools with excessive permissions
  // AI-era vulnerability categories (M5)
  | 'ai_rag_exfiltration'     // M5: RAG data leakage - cross-tenant retrieval, raw context exposure
  | 'ai_endpoint_unprotected' // M5: AI endpoints without auth/rate limiting
  | 'ai_schema_mismatch'      // M5: Missing schema validation on AI-generated outputs
  // AI Detection Roadmap Phase 1
  | 'ai_package_hallucination'  // P0: AI-hallucinated/fake package names in dependencies
  | 'ai_rag_corpus_poisoning'   // P1: User uploads directly embedded without sanitization
  | 'ai_rag_pii_leakage'        // P1: PII fields exposed in RAG retrieval responses
  | 'ai_mcp_tool_poisoning'     // P1: MCP tool with unvalidated external content
  | 'ai_mcp_credential_issue'   // P1: MCP credentials in parameters/responses
  | 'ai_mcp_confused_deputy'    // P1: MCP operations without user context
  // Phase 1 Enhancement Backlog
  | 'ai_mcp_description_injection'  // Prompt injection in MCP tool descriptions
  | 'ai_mcp_server_shadowing'       // Cross-server tool shadowing attacks
  | 'ai_mcp_config_secrets'         // Secrets in MCP config files
  | 'ai_mcp_config_permissions'     // Overpermissive MCP settings
  | 'ai_rag_query_injection'        // Query manipulation in RAG systems
  | 'ai_rag_embedding_poisoning'    // Adversarial embedding attacks
  | 'ai_rag_chunk_injection'        // Chunk boundary exploitation
  | 'ai_package_typosquat'          // Typosquatting attacks on packages
  | 'ai_package_malicious'          // Known malicious/vulnerable package
  // AI Detection Roadmap Phase 2
  | 'ai_unsafe_model_load'          // Pickle/torch.load RCE risks
  | 'ai_unverified_model'           // Model loaded without integrity verification
  | 'ai_unsafe_finetuning'          // Training on unvalidated data
  | 'ai_excessive_agency'           // Unbounded agent autonomy risks
  // Agent skill file security
  | 'ai_skill_injection'            // Agent skill file with prompt injection, data exfil, or hidden execution
  // OWASP Workstream 1: Classic vulnerability detectors
  | 'missing_security_headers'      // Missing HTTP security headers (CSP, HSTS, etc.)
  | 'ssrf'                          // Server-Side Request Forgery
  | 'log_injection'                 // Log injection / log forging
  | 'xxe'                           // XML External Entity injection

export type ValidationStatus = 'confirmed' | 'downgraded' | 'dismissed' | 'not_validated'

export interface Vulnerability {
  id: string
  filePath: string
  lineNumber: number
  lineContent: string
  severity: VulnerabilitySeverity
  category: VulnerabilityCategory
  title: string
  description: string
  suggestedFix?: string
  confidence: 'high' | 'medium' | 'low'
  /** @deprecated Use `source` instead. Kept for backward compatibility. */
  layer: 1 | 2 | 3
  /** Which detector group produced this finding */
  source?: 'secrets' | 'structural' | 'ai_code' | 'config' | 'validation'
  requiresAIValidation?: boolean // If true, must pass through AI validation before surfacing

  // AI validation metadata
  validatedByAI?: boolean // true if this finding was processed by AI validation
  validationStatus?: ValidationStatus // outcome of AI validation
  validationNotes?: string // e.g., "Route protected by Clerk middleware"
  originalSeverity?: VulnerabilitySeverity // For downgraded findings, the original severity

  // Actionable output fields (PRO-82)
  impact?: string // Why this matters (business impact) - from metadata registry or AI
  evidence?: string // What triggered this finding
  fixSteps?: string[] // Step-by-step fix instructions
  references?: string[] // OWASP/CWE documentation links
  aiEnhanced?: boolean // True if AI provided custom impact/fix beyond registry defaults

  /** Base confidence score (0.0-1.0) set by the detector. Used by scoring stage. */
  baseConfidence?: number
}

/**
 * Cancellation token for aborting scans gracefully
 * Allows users to stop long-running scans (Ctrl+C) and get partial results
 */
export interface CancellationToken {
  /** Whether cancellation has been requested */
  cancelled: boolean
  /** Reason for cancellation (e.g., "User pressed Ctrl+C") */
  reason?: string
  /** Request cancellation */
  cancel(reason?: string): void
  /** Register cleanup callback to run when cancelled */
  onCancel(callback: () => void): void
}

/**
 * Create a new cancellation token
 *
 * @example
 * const token = createCancellationToken()
 * process.on('SIGINT', () => token.cancel('User interrupted'))
 * const result = await runScan(files, repo, { cancellationToken: token })
 */
export function createCancellationToken(): CancellationToken {
  const cleanupCallbacks: Array<() => void> = []

  const token: CancellationToken = {
    cancelled: false,
    reason: undefined,
    cancel(reason?: string) {
      if (!token.cancelled) {
        token.cancelled = true
        token.reason = reason
        // Run cleanup callbacks
        cleanupCallbacks.forEach(cb => {
          try {
            cb()
          } catch (e) {
            // Ignore cleanup errors
          }
        })
      }
    },
    onCancel(callback) {
      if (token.cancelled) {
        callback() // Already cancelled, run immediately
      } else {
        cleanupCallbacks.push(callback)
      }
    }
  }

  return token
}

export interface ScanFile {
  path: string
  content: string
  language: string
  size: number
}

// Severity counts for issue-mix display
export interface SeverityCounts {
  critical: number
  high: number
  medium: number
  low: number
  info: number
}

// Category counts keyed by VulnerabilityCategory
export type CategoryCounts = Partial<Record<VulnerabilityCategory, number>>

/** Summary of a suppressed vulnerability (for --show-suppressed output) */
export interface SuppressedVulnerabilitySummary {
  /** Finding hash */
  hash: string
  /** File path */
  filePath: string
  /** Line number */
  lineNumber: number
  /** Vulnerability category */
  category: VulnerabilityCategory
  /** Severity */
  severity: VulnerabilitySeverity
  /** Title */
  title: string
  /** How it was suppressed */
  suppressionType: 'inline' | 'config-finding' | 'config-rule'
  /** Reason for suppression */
  suppressionReason: string
  /** Expiration date (if any) */
  expires?: string
}

export interface ScanResult {
  repoName: string
  repoUrl: string
  branch: string
  filesScanned: number
  filesSkipped: number
  vulnerabilities: Vulnerability[]

  // Issue-mix fields - use these for displaying security posture
  severityCounts: SeverityCounts
  categoryCounts: CategoryCounts
  hasBlockingIssues: boolean // true if any critical or high severity issues

  scanDuration: number // milliseconds
  timestamp: string

  // AI Validation stats (Phase 1 optimization metrics)
  validationStats?: {
    totalFindings: number
    validatedFindings: number
    confirmedFindings: number
    dismissedFindings: number
    downgradedFindings: number
    autoDismissedFindings: number
    estimatedInputTokens: number
    estimatedOutputTokens: number
    estimatedCost: number
    apiCalls: number
    cacheCreationTokens: number
    cacheReadTokens: number
    cacheHitRate: number
  }

  // Cancellation metadata
  cancelled?: boolean // true if scan was cancelled by user
  cancelReason?: string // Reason for cancellation (e.g., "User pressed Ctrl+C")

  // Suppression metadata
  suppressionStats?: {
    /** Total findings before suppression */
    total: number
    /** Findings suppressed by inline comments */
    inlineSuppressed: number
    /** Findings suppressed by config (finding hash) */
    configFindingSuppressed: number
    /** Findings suppressed by config (rule) */
    configRuleSuppressed: number
    /** Findings that would have been suppressed but suppression expired */
    expired: number
  }
  /** Suppressed vulnerabilities (when --show-suppressed is used) */
  suppressedVulnerabilities?: SuppressedVulnerabilitySummary[]

  // Baseline diff metadata (only present when --new flag is used)
  baselineDiff?: {
    /** When the baseline was created */
    baselineCreatedAt: string
    /** Git commit of the baseline (if available) */
    baselineCommit?: string
    /** Number of new findings (not in baseline) */
    newCount: number
    /** Number of fixed findings (in baseline, not in current) */
    fixedCount: number
    /** Number of existing findings (in both) */
    existingCount: number
    /** Details of fixed findings for display */
    fixedFindings: Array<{
      hash: string
      filePath: string
      lineNumber: number
      category: string
      severity: string
      title: string
    }>
  }

  /** Filter audit trail (when includeFilterAudit option is enabled) */
  filterAuditTrail?: import('../postprocess/filtering/pipeline').AnnotatedFinding[]
}

export interface ScanProgress {
  status: 'fetching' | 'layer1' | 'layer2' | 'layer3' | 'validating' | 'complete' | 'failed'
  message: string
  filesProcessed: number
  totalFiles: number
  vulnerabilitiesFound: number
}

export type ProgressCallback = (progress: ScanProgress) => void

// Pattern definitions for Layer 1
export interface SecretPattern {
  name: string
  pattern: RegExp
  severity: VulnerabilitySeverity
  description: string
}

// Config audit rules for Layer 1
export interface ConfigRule {
  name: string
  filePatterns: string[]
  check: (content: string, filePath: string) => ConfigViolation[]
}

export interface ConfigViolation {
  line: number
  lineContent: string
  message: string
  severity: VulnerabilitySeverity
}

// Layer 2 heuristics
export interface SensitiveVariablePattern {
  pattern: RegExp
  severity: VulnerabilitySeverity
  description: string
}

// Layer 3 AI analysis
export interface AIAnalysisRequest {
  filePath: string
  content: string
  context: string
}

export interface AIFinding {
  lineNumber: number
  lineContent: string
  severity: VulnerabilitySeverity
  category: VulnerabilityCategory
  title: string
  description: string
  suggestedFix: string
}

// ============================================================================
// Detector Context (for Layer 2 context-aware detection)
// ============================================================================

/**
 * DetectorContext provides rich context information to Layer 2 detectors
 * This allows detectors to make smarter decisions based on:
 * - Project-level auth configuration
 * - Framework detection
 * - Per-file context (server vs client, test vs production)
 */
export interface DetectorContext {
  // Auth context (project-wide)
  auth: {
    /** Middleware configuration (if detected) */
    middleware: MiddlewareAuthContextSummary | null
    /** Auth helpers detected in the project */
    authHelpers: AuthHelperContextSummary
    /** Public endpoint patterns (health, webhook, etc.) */
    publicEndpoints: string[]
  }

  // Framework context (project-wide)
  framework: {
    /** Primary backend framework detected */
    primary: 'nextjs' | 'express' | 'fastify' | 'koa' | 'hono' | 'nestjs' | 'django' | 'flask' | 'rails' | 'unknown' | null
    /** Frontend framework detected */
    frontend: 'react' | 'vue' | 'svelte' | 'angular' | 'unknown' | null
    /** Whether the project uses TypeScript */
    hasTypeScript: boolean
  }

  // Data access context (project-wide)
  dataAccess: {
    /** ORM/query builder detected */
    orm: 'prisma' | 'drizzle' | 'typeorm' | 'sequelize' | 'knex' | null
    /** Whether RLS (Row-Level Security) is detected */
    hasRLS: boolean
    /** Validation library detected */
    validationLib: 'zod' | 'yup' | 'joi' | 'valibot' | null
  }

  // Per-file context (computed for each file)
  file: {
    /** Whether this file is server-only (not bundled to client) */
    isServerOnly: boolean
    /** Whether this file is client-bundled (exposed to browser) */
    isClientBundled: boolean
    /** Whether this file is a test/mock/fixture file */
    isTestFile: boolean
    /** Whether this file is a config file */
    isConfigFile: boolean
    /** Whether this file is in a tooling directory */
    isToolingDir: boolean
  }
}

/** Simplified middleware auth config for DetectorContext */
export interface MiddlewareAuthContextSummary {
  /** Whether auth middleware was detected */
  hasAuthMiddleware: boolean
  /** The type of auth detected */
  authType: 'clerk' | 'nextauth' | 'auth0' | 'supabase' | 'custom' | 'unknown' | null
  /** Protected path patterns */
  protectedPaths: string[]
  /** Public/excluded path patterns */
  publicPaths: string[]
}

/** Simplified auth helper context for DetectorContext */
export interface AuthHelperContextSummary {
  /** Whether the project has throwing auth helpers */
  hasThrowingHelpers: boolean
  /** Names of throwing auth helpers */
  throwingHelperNames: string[]
}

/**
 * Create an empty/default DetectorContext
 * Useful for tests or when context isn't available
 */
export function createEmptyDetectorContext(): DetectorContext {
  return {
    auth: {
      middleware: null,
      authHelpers: {
        hasThrowingHelpers: false,
        throwingHelperNames: [],
      },
      publicEndpoints: [],
    },
    framework: {
      primary: null,
      frontend: null,
      hasTypeScript: false,
    },
    dataAccess: {
      orm: null,
      hasRLS: false,
      validationLib: null,
    },
    file: {
      isServerOnly: false,
      isClientBundled: false,
      isTestFile: false,
      isConfigFile: false,
      isToolingDir: false,
    },
  }
}

// Supported file extensions for scanning
export const SCANNABLE_EXTENSIONS = [
  '.js', '.jsx', '.ts', '.tsx', '.mjs', '.cjs',
  '.py', '.rb', '.php', '.go', '.java', '.cs',
  '.env', '.yaml', '.yml', '.json', '.toml',
  '.dockerfile', '.sh', '.bash',
  '.md',  // Agent skill files (SKILL.md, copilot-instructions.md, etc.)
]

// Files to always scan regardless of extension
export const SPECIAL_FILES = [
  'Dockerfile',
  'docker-compose.yml',
  'docker-compose.yaml',
  '.env',
  '.env.local',
  '.env.production',
  '.env.development',
  'package.json',
  'requirements.txt',
  'Gemfile',
  'go.mod',
  '.cursorrules',
]

// Max file size to scan (50KB as per PRD)
export const MAX_FILE_SIZE = 50 * 1024

// ============================================================================
// Scan Mode Configuration
// ============================================================================

/**
 * Scan mode determines the depth and cost of the scan
 * 
 * - full: Complete scan with AI validation on all files (initial onboarding, deep audits)
 * - incremental: Focused scan on changed files only (CI/CD, fast feedback)
 */
export type ScanMode = 'full' | 'incremental'

/**
 * Scan depth controls AI usage independent of full vs incremental mode
 *
 * - local: Layer 1 + Layer 2 only. No AI validation, no Layer 3.
 *          Only Tier A (core) findings are surfaced.
 *          Target: <5s for typical PR scans.
 *
 * - verified: Layer 1 + Layer 2 + AI validation on selected findings. No Layer 3.
 *             Tier A is surfaced directly, Tier B goes through AI validation.
 *             Target: <15s with <10 AI calls.
 *
 * - deep: Layer 1 + Layer 2 + AI validation + Layer 3 semantic analysis.
 *         Full analysis for initial onboarding or deep security audits.
 *         Target: Complete thoroughness, cost secondary.
 *
 * ## Workflow Profile Recommendations:
 *
 * | Workflow       | Default Depth | Rationale                            |
 * |----------------|---------------|--------------------------------------|
 * | GitHub PR      | local         | Fast feedback, high-signal findings  |
 * | VS Code        | verified      | Interactive, balance depth + speed   |
 * | CLI (default)  | local         | Fast local scans                     |
 * | CLI --deep     | deep          | Thorough analysis when requested     |
 * | Onboarding     | deep          | Full picture on first scan           |
 */
export type ScanDepth = 'local' | 'verified' | 'deep'

export interface ScanModeConfig {
  /** The scan mode */
  mode: ScanMode
  
  /** For incremental scans: paths of changed files */
  changedFiles?: string[]
  
  /** For incremental scans: base commit/branch to diff against */
  baseBranch?: string
  
  /** Whether to skip AI validation entirely (for very fast scans) */
  skipAIValidation?: boolean
  
  /** Whether to skip Layer 3 deep analysis (reduces cost) */
  skipLayer3?: boolean
  
  /** Maximum files to send to AI validation (cost control) */
  maxAIValidationFiles?: number
  
  /** Maximum files for Layer 3 analysis (cost control) */
  maxLayer3Files?: number

  /** Scan depth mode (cheap/validated/deep) - controls AI usage */
  scanDepth?: ScanDepth

  /** Whether to exclude test files from scanning (default: true) */
  excludeTestFiles?: boolean

  /** Whether to exclude seed files from scanning (default: true) */
  excludeSeedFiles?: boolean

  /** Custom file path patterns to exclude (glob format) */
  customExclusions?: string[]
}

/**
 * Default configurations for each scan mode
 */
export const SCAN_MODE_DEFAULTS: Record<ScanMode, Partial<ScanModeConfig>> = {
  full: {
    mode: 'full',
    skipAIValidation: false,
    skipLayer3: false,
    maxAIValidationFiles: 50,
    maxLayer3Files: 15,
  },
  incremental: {
    mode: 'incremental',
    skipAIValidation: false,
    skipLayer3: true, // Skip expensive Layer 3 for incremental
    maxAIValidationFiles: 20,
    maxLayer3Files: 5,
  },
}
