/**
 * Scan Depth Snapshot Tests
 *
 * Tests the scanner output at different depth levels to ensure consistent results.
 * Snapshots help detect unintended changes to scanner behavior.
 *
 * Run: npx jest src/__tests__/snapshots/scan-depth.test.ts
 * Update snapshots: npx jest --updateSnapshot
 */

import { runLayer1Scan } from '../../detect/secrets'
import { runLayer2Scan } from '../../detect/structural'
import type { ScanFile, Vulnerability } from '../../shared/types'

// Helper to normalize vulnerability output for snapshot comparison
// Removes volatile fields like timestamps and IDs
function normalizeVulnerabilities(vulns: Vulnerability[]): object[] {
  return vulns
    .map(v => ({
      category: v.category,
      severity: v.severity,
      confidence: v.confidence,
      layer: v.layer,
      title: v.title,
      lineNumber: v.lineNumber,
      // Normalize line content to avoid whitespace issues
      lineContent: v.lineContent?.trim(),
    }))
    .sort((a, b) => {
      // Sort by line number, then category for consistent ordering
      if (a.lineNumber !== b.lineNumber) return (a.lineNumber || 0) - (b.lineNumber || 0)
      return a.category.localeCompare(b.category)
    })
}

// Helper to run a cheap scan (Layer 1 only, no AI)
async function runCheapScan(file: ScanFile): Promise<object[]> {
  const layer1Result = await runLayer1Scan([file])
  return normalizeVulnerabilities(layer1Result.vulnerabilities)
}

// Helper to run a full scan (Layer 1 + Layer 2, no AI validation)
async function runFullScan(file: ScanFile): Promise<object[]> {
  const [layer1Result, layer2Result] = await Promise.all([
    runLayer1Scan([file]),
    runLayer2Scan([file]),
  ])
  return normalizeVulnerabilities([...layer1Result.vulnerabilities, ...layer2Result.vulnerabilities])
}

describe('Scan Depth Snapshots', () => {
  describe('Clean File - No Findings Expected', () => {
    const cleanFile: ScanFile = {
      path: 'src/utils/helpers.ts',
      content: `
/**
 * Safe utility functions
 */

export function formatDate(date: Date): string {
  return date.toISOString()
}

export function capitalize(str: string): string {
  return str.charAt(0).toUpperCase() + str.slice(1)
}

export function clamp(value: number, min: number, max: number): number {
  return Math.min(Math.max(value, min), max)
}
`,
      language: 'typescript',
      size: 300,
    }

    it('cheap scan should produce no findings', async () => {
      const results = await runCheapScan(cleanFile)
      expect(results).toMatchSnapshot()
    })

    it('full scan should produce no findings', async () => {
      const results = await runFullScan(cleanFile)
      expect(results).toMatchSnapshot()
    })
  })

  describe('Hardcoded Secrets - Critical Findings', () => {
    const secretsFile: ScanFile = {
      path: 'src/config/api-keys.ts',
      content: `
// API Keys - CRITICAL
const OPENAI_API_KEY = "sk-proj-abc123def456ghi789jkl012mno345pqr678stu901vwx"
const AWS_ACCESS_KEY_ID = "AKIAIOSFODNN7EXAMPLE"
const AWS_SECRET_ACCESS_KEY = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
`,
      language: 'typescript',
      size: 250,
    }

    it('cheap scan should detect hardcoded secrets', async () => {
      const results = await runCheapScan(secretsFile)
      expect(results).toMatchSnapshot()
    })

    it('full scan should detect hardcoded secrets', async () => {
      const results = await runFullScan(secretsFile)
      expect(results).toMatchSnapshot()
    })
  })

  describe('Mixed Severity - Multiple Issue Types', () => {
    const mixedFile: ScanFile = {
      path: 'src/api/handler.ts',
      content: `
import express from 'express'

const app = express()

// Hardcoded secret - CRITICAL
const API_KEY = "sk-live-1234567890abcdef"

// CORS wildcard - MEDIUM
app.use(cors({ origin: '*' }))

// Weak crypto - HIGH
const hash = crypto.createHash('md5')

// innerHTML with user input - HIGH
element.innerHTML = userInput

// Debug logging - LOW
console.log('Debug:', sensitiveData)
`,
      language: 'typescript',
      size: 400,
    }

    it('cheap scan should detect Layer 1 issues', async () => {
      const results = await runCheapScan(mixedFile)
      expect(results).toMatchSnapshot()
    })

    it('full scan should detect all issues', async () => {
      const results = await runFullScan(mixedFile)
      expect(results).toMatchSnapshot()
    })
  })

  describe('AI-Era Patterns - Modern Security Issues', () => {
    const aiEraFile: ScanFile = {
      path: 'src/ai/chat-handler.ts',
      content: `
import OpenAI from 'openai'

// User API key in request - transient BYOK
export async function handleChat(req: Request) {
  const openai = new OpenAI({ apiKey: req.body.userApiKey })

  // User input directly in prompt - prompt injection risk
  const response = await openai.chat.completions.create({
    model: 'gpt-4',
    messages: [{ role: 'user', content: req.body.message }],
  })

  return response.choices[0].message
}
`,
      language: 'typescript',
      size: 400,
    }

    it('cheap scan should detect Layer 1 patterns', async () => {
      const results = await runCheapScan(aiEraFile)
      expect(results).toMatchSnapshot()
    })

    it('full scan should detect AI-era security patterns', async () => {
      const results = await runFullScan(aiEraFile)
      expect(results).toMatchSnapshot()
    })
  })

  describe('Auth Patterns - Missing Authentication', () => {
    const authFile: ScanFile = {
      path: 'src/app/api/users/route.ts',
      content: `
import { NextResponse } from 'next/server'
import { db } from '@/lib/db'

// API route without auth check
export async function GET(request: Request) {
  const users = await db.user.findMany()
  return NextResponse.json(users)
}

export async function DELETE(request: Request) {
  const { id } = await request.json()
  await db.user.delete({ where: { id } })
  return NextResponse.json({ success: true })
}
`,
      language: 'typescript',
      size: 350,
    }

    it('cheap scan should detect basic patterns', async () => {
      const results = await runCheapScan(authFile)
      expect(results).toMatchSnapshot()
    })

    it('full scan should detect missing auth patterns', async () => {
      const results = await runFullScan(authFile)
      expect(results).toMatchSnapshot()
    })
  })

  describe('Safe File - No False Positives', () => {
    const safeFile: ScanFile = {
      path: 'src/components/Dashboard.tsx',
      content: `
import { useState, useEffect } from 'react'

// CSS classes - should NOT be flagged as high entropy
const styles = 'flex items-center justify-between p-4 bg-gray-100'

// Environment variable reference - should NOT be flagged
const apiUrl = process.env.API_URL

// UUID - should NOT be flagged as secret
const sessionId = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890'

export function Dashboard() {
  const [data, setData] = useState(null)

  useEffect(() => {
    fetch(apiUrl + '/data')
      .then(res => res.json())
      .then(setData)
  }, [])

  return <div className={styles}>{data}</div>
}
`,
      language: 'typescript',
      size: 500,
    }

    it('cheap scan should produce minimal findings', async () => {
      const results = await runCheapScan(safeFile)
      expect(results).toMatchSnapshot()
    })

    it('full scan should produce minimal findings', async () => {
      const results = await runFullScan(safeFile)
      expect(results).toMatchSnapshot()
    })
  })
})
