/**
 * IDE Integration Tests
 */

import { existsSync, mkdirSync, writeFileSync, rmSync } from 'fs'
import { join } from 'path'
import { tmpdir } from 'os'
import {
  formatCursorRules,
  formatWindsurfRules,
  formatClaudeCodeSection,
  detectIDEConfigs,
  writeIDEFile,
  updateClaudeMdSection,
  clearIDEFiles,
} from '../index'
import type { ScanResult, Vulnerability } from '../../../types'

const createMockVulnerability = (overrides: Partial<Vulnerability> = {}): Vulnerability => ({
  id: 'test-vuln-1',
  filePath: 'src/api/users.ts',
  lineNumber: 42,
  lineContent: 'const query = `SELECT * FROM users WHERE id = ${userId}`',
  severity: 'high',
  category: 'sql_injection',
  title: 'SQL Injection Vulnerability',
  description: 'User input is directly concatenated into SQL query without parameterization.',
  suggestedFix: 'Use parameterized queries instead of string concatenation.',
  confidence: 'high',
  layer: 2,
  fixSteps: [
    'Replace string concatenation with parameterized query',
    'Use prepared statements or an ORM',
  ],
  ...overrides,
})

const createMockScanResult = (vulnerabilities: Vulnerability[] = []): ScanResult => ({
  repoName: 'test-repo',
  repoUrl: 'https://github.com/test/test-repo',
  branch: 'main',
  filesScanned: 50,
  filesSkipped: 5,
  vulnerabilities,
  severityCounts: {
    critical: vulnerabilities.filter(v => v.severity === 'critical').length,
    high: vulnerabilities.filter(v => v.severity === 'high').length,
    medium: vulnerabilities.filter(v => v.severity === 'medium').length,
    low: vulnerabilities.filter(v => v.severity === 'low').length,
    info: vulnerabilities.filter(v => v.severity === 'info').length,
  },
  categoryCounts: {},
  hasBlockingIssues: vulnerabilities.some(v => v.severity === 'critical' || v.severity === 'high'),
  scanDuration: 1500,
  timestamp: '2024-01-15T10:30:00.000Z',
})

describe('formatCursorRules', () => {
  it('should generate MDC format with frontmatter', () => {
    const vuln = createMockVulnerability()
    const result = createMockScanResult([vuln])

    const output = formatCursorRules(result)

    // Check frontmatter
    expect(output).toContain('---')
    expect(output).toContain('description: Oculum Security Findings')
    expect(output).toContain('alwaysApply: false')
    expect(output).toContain('---')
  })

  it('should include glob patterns for affected files', () => {
    const vulns = [
      createMockVulnerability({ filePath: 'src/api/users.ts' }),
      createMockVulnerability({ id: 'v2', filePath: 'src/api/orders.ts' }),
      createMockVulnerability({ id: 'v3', filePath: 'src/config/db.ts' }),
    ]
    const result = createMockScanResult(vulns)

    const output = formatCursorRules(result)

    // Should include globs for affected directories
    expect(output).toContain('globs:')
    expect(output).toContain('src/api/**')
    expect(output).toContain('src/config/**')
  })

  it('should include security findings', () => {
    const vuln = createMockVulnerability()
    const result = createMockScanResult([vuln])

    const output = formatCursorRules(result)

    expect(output).toContain('SQL Injection Vulnerability')
    expect(output).toContain('src/api/users.ts:42')
  })

  it('should include fix instructions', () => {
    const vuln = createMockVulnerability()
    const result = createMockScanResult([vuln])

    const output = formatCursorRules(result)

    expect(output).toContain('DO NOT')
    // The fix shows example code with '?' instead of the word 'parameterized'
    expect(output).toContain('GOOD')
    expect(output).toContain("'SELECT * FROM users WHERE id = ?'")
  })

  it('should return minimal output for zero findings', () => {
    const result = createMockScanResult([])

    const output = formatCursorRules(result)

    expect(output).toContain('description: Oculum Security Findings')
    expect(output).toContain('No security issues found')
  })
})

describe('formatWindsurfRules', () => {
  it('should generate markdown security rules', () => {
    const vuln = createMockVulnerability()
    const result = createMockScanResult([vuln])

    const output = formatWindsurfRules(result)

    expect(output).toContain('# Oculum Security Rules')
    expect(output).toContain('## Security Issues')
  })

  it('should list findings by file', () => {
    const vulns = [
      createMockVulnerability({ filePath: 'src/api/users.ts' }),
      createMockVulnerability({ id: 'v2', filePath: 'src/api/orders.ts' }),
    ]
    const result = createMockScanResult(vulns)

    const output = formatWindsurfRules(result)

    expect(output).toContain('src/api/users.ts')
    expect(output).toContain('src/api/orders.ts')
    expect(output).toContain('Line 42')
  })

  it('should include code patterns section', () => {
    const vuln = createMockVulnerability()
    const result = createMockScanResult([vuln])

    const output = formatWindsurfRules(result)

    expect(output).toContain('## Code Patterns')
    expect(output).toContain('ALWAYS')
  })
})

describe('formatClaudeCodeSection', () => {
  it('should return section with markers', () => {
    const vuln = createMockVulnerability()
    const result = createMockScanResult([vuln])

    const output = formatClaudeCodeSection(result)

    expect(output).toContain('<!-- OCULUM_SECURITY_START -->')
    expect(output).toContain('<!-- OCULUM_SECURITY_END -->')
  })

  it('should include security issues', () => {
    const vuln = createMockVulnerability()
    const result = createMockScanResult([vuln])

    const output = formatClaudeCodeSection(result)

    expect(output).toContain('## Security Issues')
    expect(output).toContain('SQL Injection')
    expect(output).toContain('src/api/users.ts:42')
  })

  it('should include verification instructions', () => {
    const vuln = createMockVulnerability()
    const result = createMockScanResult([vuln])

    const output = formatClaudeCodeSection(result)

    expect(output).toContain('oculum scan')
  })
})

describe('detectIDEConfigs', () => {
  const testDir = join(tmpdir(), 'oculum-ide-detect-test-' + Date.now())

  beforeAll(() => {
    mkdirSync(testDir, { recursive: true })
  })

  afterAll(() => {
    try {
      rmSync(testDir, { recursive: true, force: true })
    } catch {
      // Ignore cleanup errors
    }
  })

  it('should detect .cursor directory', () => {
    // Create .cursor directory
    mkdirSync(join(testDir, '.cursor'), { recursive: true })

    const detected = detectIDEConfigs(testDir)

    expect(detected).toContain('cursor')
  })

  it('should detect CLAUDE.md file', () => {
    // Create CLAUDE.md file
    writeFileSync(join(testDir, 'CLAUDE.md'), '# Claude Code\n')

    const detected = detectIDEConfigs(testDir)

    expect(detected).toContain('claude-code')
  })

  it('should return array of detected IDEs', () => {
    const detected = detectIDEConfigs(testDir)

    expect(Array.isArray(detected)).toBe(true)
  })
})

describe('IDE file operations', () => {
  const testDir = join(tmpdir(), 'oculum-ide-ops-test-' + Date.now())

  beforeAll(() => {
    mkdirSync(testDir, { recursive: true })
  })

  afterAll(() => {
    try {
      rmSync(testDir, { recursive: true, force: true })
    } catch {
      // Ignore cleanup errors
    }
  })

  it('should write IDE file and create directories', () => {
    const result = writeIDEFile(
      testDir,
      '.cursor/rules/security.mdc',
      '# Security Rules'
    )

    expect(result.success).toBe(true)
    expect(existsSync(join(testDir, '.cursor/rules/security.mdc'))).toBe(true)
  })

  it('should update CLAUDE.md section between markers', () => {
    // Create initial CLAUDE.md
    const initialContent = `# Project

Some existing content.

<!-- OCULUM_SECURITY_START -->
Old security content
<!-- OCULUM_SECURITY_END -->

More content.
`
    writeFileSync(join(testDir, 'CLAUDE.md'), initialContent)

    // Update section
    const result = updateClaudeMdSection(
      testDir,
      '<!-- OCULUM_SECURITY_START -->\nNew security content\n<!-- OCULUM_SECURITY_END -->'
    )

    expect(result.success).toBe(true)

    // Read and verify
    const { readFileSync } = require('fs')
    const updated = readFileSync(join(testDir, 'CLAUDE.md'), 'utf-8')

    expect(updated).toContain('New security content')
    expect(updated).not.toContain('Old security content')
    expect(updated).toContain('Some existing content')
    expect(updated).toContain('More content')
  })

  it('should append section if no markers exist', () => {
    // Create CLAUDE.md without markers
    const noMarkersDir = join(testDir, 'no-markers')
    mkdirSync(noMarkersDir, { recursive: true })
    writeFileSync(join(noMarkersDir, 'CLAUDE.md'), '# Project\n\nSome content.\n')

    const result = updateClaudeMdSection(
      noMarkersDir,
      '<!-- OCULUM_SECURITY_START -->\nSecurity content\n<!-- OCULUM_SECURITY_END -->'
    )

    expect(result.success).toBe(true)

    const { readFileSync } = require('fs')
    const updated = readFileSync(join(noMarkersDir, 'CLAUDE.md'), 'utf-8')

    expect(updated).toContain('Security content')
    expect(updated).toContain('Some content')
  })

  it('should clear IDE files', () => {
    // Create some IDE files
    const clearDir = join(testDir, 'clear-test')
    mkdirSync(join(clearDir, '.cursor/rules'), { recursive: true })
    writeFileSync(join(clearDir, '.cursor/rules/security.mdc'), 'content')
    writeFileSync(join(clearDir, '.windsurfrules'), 'content')

    const result = clearIDEFiles(clearDir)

    expect(result.success).toBe(true)
    expect(existsSync(join(clearDir, '.cursor/rules/security.mdc'))).toBe(false)
    expect(existsSync(join(clearDir, '.windsurfrules'))).toBe(false)
  })
})
