/**
 * Tests for the context extractor utility.
 *
 * Verifies:
 * - Small files get full content
 * - Large files get scoped content with omission markers
 * - Adjacent findings merge ranges
 * - High coverage falls back to full file
 * - Edge cases (line 1, last line, empty)
 * - mergeRanges helper
 */

import { extractRelevantContext, mergeRanges } from '../utils/context-extractor'

// ============================================================================
// Helpers
// ============================================================================

function generateFile(lineCount: number): string {
  return Array.from({ length: lineCount }, (_, i) => `line ${i + 1} content`).join('\n')
}

// ============================================================================
// mergeRanges
// ============================================================================

describe('mergeRanges', () => {
  test('empty input returns empty', () => {
    expect(mergeRanges([])).toEqual([])
  })

  test('single range returns unchanged', () => {
    expect(mergeRanges([{ start: 5, end: 10 }])).toEqual([{ start: 5, end: 10 }])
  })

  test('overlapping ranges merge', () => {
    const result = mergeRanges([
      { start: 1, end: 10 },
      { start: 5, end: 15 },
    ])
    expect(result).toEqual([{ start: 1, end: 15 }])
  })

  test('adjacent ranges merge', () => {
    const result = mergeRanges([
      { start: 1, end: 10 },
      { start: 11, end: 20 },
    ])
    expect(result).toEqual([{ start: 1, end: 20 }])
  })

  test('disjoint ranges remain separate', () => {
    const result = mergeRanges([
      { start: 1, end: 10 },
      { start: 20, end: 30 },
    ])
    expect(result).toEqual([
      { start: 1, end: 10 },
      { start: 20, end: 30 },
    ])
  })

  test('unsorted input gets sorted and merged', () => {
    const result = mergeRanges([
      { start: 20, end: 30 },
      { start: 1, end: 10 },
      { start: 5, end: 25 },
    ])
    expect(result).toEqual([{ start: 1, end: 30 }])
  })

  test('multiple separate ranges', () => {
    const result = mergeRanges([
      { start: 1, end: 5 },
      { start: 10, end: 15 },
      { start: 20, end: 25 },
    ])
    expect(result).toEqual([
      { start: 1, end: 5 },
      { start: 10, end: 15 },
      { start: 20, end: 25 },
    ])
  })
})

// ============================================================================
// extractRelevantContext
// ============================================================================

describe('extractRelevantContext', () => {
  test('small file (50 lines) returns full content', () => {
    const content = generateFile(50)
    const result = extractRelevantContext(content, [25])
    expect(result.isFullFile).toBe(true)
    expect(result.linesSent).toBe(50)
    expect(result.totalLines).toBe(50)
    expect(result.content).toContain('line 1 content')
    expect(result.content).toContain('line 50 content')
    expect(result.content).not.toContain('lines omitted')
  })

  test('large file with single finding returns scoped content', () => {
    const content = generateFile(300)
    const result = extractRelevantContext(content, [150])
    expect(result.isFullFile).toBe(false)
    expect(result.totalLines).toBe(300)
    expect(result.linesSent).toBeLessThan(300)
    // Should contain the finding line
    expect(result.content).toContain('line 150 content')
    // Should contain imports (first 15 lines)
    expect(result.content).toContain('line 1 content')
    expect(result.content).toContain('line 15 content')
    // Should have omission markers
    expect(result.content).toContain('lines omitted')
  })

  test('adjacent findings merge ranges (no omission between them)', () => {
    const content = generateFile(300)
    const result = extractRelevantContext(content, [100, 110])
    // Lines 100 and 110 are within padding distance, so should merge
    expect(result.content).toContain('line 100 content')
    expect(result.content).toContain('line 110 content')
    // The region between 100 and 110 should NOT have omission
    const lines = result.content.split('\n')
    const between = lines.filter(l =>
      l.includes('lines omitted') &&
      l.includes('100') &&
      l.includes('110')
    )
    expect(between).toHaveLength(0)
  })

  test('high coverage falls back to full file', () => {
    const content = generateFile(200)
    // 3 findings spread across the file covering >60%
    const result = extractRelevantContext(content, [30, 100, 170])
    expect(result.isFullFile).toBe(true)
    expect(result.linesSent).toBe(200)
  })

  test('finding at line 1', () => {
    const content = generateFile(300)
    const result = extractRelevantContext(content, [1])
    expect(result.content).toContain('line 1 content')
    // Should not have negative line references
    expect(result.content).not.toContain('line 0')
    expect(result.content).not.toContain('line -')
  })

  test('finding at last line', () => {
    const content = generateFile(300)
    const result = extractRelevantContext(content, [300])
    expect(result.content).toContain('line 300 content')
  })

  test('empty file returns full content', () => {
    const result = extractRelevantContext('', [1])
    expect(result.isFullFile).toBe(true)
    expect(result.totalLines).toBe(1) // empty string splits to ['']
  })

  test('empty findings returns full content', () => {
    const content = generateFile(300)
    const result = extractRelevantContext(content, [])
    expect(result.isFullFile).toBe(true)
  })

  test('custom config: smaller padding', () => {
    const content = generateFile(300)
    const result = extractRelevantContext(content, [150], { padding: 5 })
    expect(result.isFullFile).toBe(false)
    // Should have much fewer lines with small padding
    expect(result.linesSent).toBeLessThan(50)
    expect(result.content).toContain('line 150 content')
  })

  test('content includes line numbers in correct format', () => {
    const content = generateFile(300)
    const result = extractRelevantContext(content, [150])
    // Should have "   1 | line 1 content" format
    expect(result.content).toMatch(/^\s+1 \| line 1 content/m)
    expect(result.content).toMatch(/\s+150 \| line 150 content/m)
  })

  test('omission markers show correct line ranges', () => {
    const content = generateFile(300)
    const result = extractRelevantContext(content, [150], { padding: 10, importLines: 5 })
    // Should have omission markers with line ranges
    expect(result.content).toMatch(/\[\.\.\..*lines omitted.*\]/)
  })
})
