/**
 * Traceability Checker Tests
 * Comprehensive test suite for requirements traceability system
 *
 * Test Coverage:
 * - ID extraction (consolidated)
 * - Link building
 * - Orphan detection
 * - Coverage analysis
 * - Matrix generation
 * - Reporting
 * - Validation
 * - Maintenance
 *
 * Target: 35-45 tests, >85% coverage
 */

import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { IDExtractor } from '../../../src/traceability/id-extractor.ts';
import { MatrixGenerator } from '../../../src/traceability/matrix-generator.ts';
import { TraceabilityChecker } from '../../../src/traceability/traceability-checker.ts';
import { FilesystemSandbox } from '../../../src/testing/mocks/filesystem-sandbox.ts';

describe('IDExtractor', () => {
  let extractor: IDExtractor;

  beforeEach(() => {
    extractor = new IDExtractor();
  });

  describe('extractFromLine', () => {
    it('should extract all ID types from lines', () => {
      const testCases = [
        { line: '// Implements UC-001: Validate AI-Generated Content', expectedId: 'UC-001', expectedType: 'use-case' },
        { line: '// NFR-PERF-001: Context loading <5s', expectedId: 'NFR-PERF-001', expectedType: 'nfr' },
        { line: '// Covers: US-042', expectedId: 'US-042', expectedType: 'user-story' },
        { line: '// Feature F-005: Requirements Traceability', expectedId: 'F-005', expectedType: 'feature' },
        { line: '// AC-012: Must complete within 5 seconds', expectedId: 'AC-012', expectedType: 'acceptance-criteria' },
      ];

      for (const { line, expectedId, expectedType } of testCases) {
        const ids = extractor.extractFromLine(line, 1);
        expect(ids, `failed for: ${line}`).toHaveLength(1);
        expect(ids[0].id, `wrong id for: ${line}`).toBe(expectedId);
        expect(ids[0].type, `wrong type for: ${line}`).toBe(expectedType);
        expect(ids[0].lineNumber).toBe(1);
      }
    });

    it('should extract multiple IDs from one line', () => {
      const line = '// @traceability UC-001, NFR-ACC-001, F-003';
      const ids = extractor.extractFromLine(line);

      expect(ids).toHaveLength(3);
      expect(ids[0].id).toBe('UC-001');
      expect(ids[1].id).toBe('NFR-ACC-001');
      expect(ids[2].id).toBe('F-003');
    });

    it('should deduplicate IDs on same line', () => {
      const line = '// UC-001 and UC-001 again';
      const ids = extractor.extractFromLine(line);

      expect(ids).toHaveLength(1);
      expect(ids[0].id).toBe('UC-001');
    });

    it('should extract context around ID', () => {
      const line = '// This function implements UC-001 for validation';
      const ids = extractor.extractFromLine(line);

      expect(ids[0].context).toContain('UC-001');
      expect(ids[0].context).toContain('implements');
    });

    it('should handle lines without IDs and test descriptions', () => {
      const noIdLine = '// Just a regular comment';
      expect(extractor.extractFromLine(noIdLine)).toHaveLength(0);

      const testLine = `  it('should validate UC-001: AI Pattern Detection', () => {`;
      const ids = extractor.extractFromLine(testLine);
      expect(ids).toHaveLength(1);
      expect(ids[0].id).toBe('UC-001');
    });
  });

  describe('extractFromContent', () => {
    it('should extract IDs from multiline content', () => {
      const content = `
/**
 * ValidationEngine - Implements UC-001
 * Performance target: NFR-PERF-001
 * Accuracy target: NFR-ACC-001
 */
export class ValidationEngine {
  // Covers: F-001
}
      `.trim();

      const ids = extractor.extractFromContent(content);

      expect(ids.length).toBeGreaterThanOrEqual(4);
      expect(ids.map(id => id.id)).toContain('UC-001');
      expect(ids.map(id => id.id)).toContain('NFR-PERF-001');
      expect(ids.map(id => id.id)).toContain('NFR-ACC-001');
      expect(ids.map(id => id.id)).toContain('F-001');
    });

    it('should deduplicate IDs across lines', () => {
      const content = `
// UC-001
// UC-001 again
// UC-001 third time
      `.trim();

      const ids = extractor.extractFromContent(content);

      expect(ids).toHaveLength(1);
      expect(ids[0].id).toBe('UC-001');
    });

    it('should preserve line numbers', () => {
      const content = `Line 1
Line 2
// UC-001 on line 3
Line 4`;

      const ids = extractor.extractFromContent(content);

      expect(ids[0].lineNumber).toBe(3);
    });

    it('should handle empty content and content with no IDs', () => {
      expect(extractor.extractFromContent('')).toHaveLength(0);

      const noIdContent = `
export class Foo {
  bar() {
    return 42;
  }
}
      `.trim();

      expect(extractor.extractFromContent(noIdContent)).toHaveLength(0);
    });
  });

  describe('extractFromFiles', () => {
    it('should extract IDs from multiple files in parallel', async () => {
      const files = new Map<string, string>();
      files.set('file1.ts', '// UC-001: First file');
      files.set('file2.ts', '// UC-002: Second file\n// NFR-PERF-001: Performance');
      files.set('file3.ts', '// F-003: Third file');

      const results = await extractor.extractFromFiles(files);

      expect(results.size).toBe(3);
      expect(results.get('file1.ts')?.ids).toHaveLength(1);
      expect(results.get('file2.ts')?.ids).toHaveLength(2);
      expect(results.get('file3.ts')?.ids).toHaveLength(1);
    });

    it('should track extraction time per file', async () => {
      const files = new Map<string, string>();
      files.set('file1.ts', '// UC-001');

      const results = await extractor.extractFromFiles(files);
      const result = results.get('file1.ts')!;

      expect(result.extractionTime).toBeGreaterThan(0);
    });

    it('should handle empty file map', async () => {
      const files = new Map<string, string>();
      const results = await extractor.extractFromFiles(files);

      expect(results.size).toBe(0);
    });
  });

  describe('isValidId', () => {
    it('should validate all ID types and formats', () => {
      const validCases = [
        { id: 'UC-001', type: 'use-case' },
        { id: 'UC-999', type: 'use-case' },
        { id: 'NFR-PERF-001', type: 'nfr' },
        { id: 'NFR-SECURITY-999', type: 'nfr' },
        { id: 'US-001', type: 'user-story' },
        { id: 'F-001', type: 'feature' },
      ];

      for (const { id, type } of validCases) {
        expect(extractor.isValidId(id), `should be valid: ${id} (${type})`).toBe(true);
      }

      const invalidCases = [
        'UC-1',       // Too short
        'UC-1234',    // Too long
        'NFR-P-001',  // NFR category too short
        'NFR-VERYLONGCATEGORY-001', // NFR category too long
        'US-1',       // US number too short
        'F-1',        // Feature number too short
        'invalid',    // No format match
        'UC001',      // Missing dash
        'UC-',        // Missing number
      ];

      for (const id of invalidCases) {
        expect(extractor.isValidId(id), `should be invalid: ${id}`).toBe(false);
      }
    });
  });

  describe('parseId', () => {
    it('should parse all ID formats correctly', () => {
      const testCases = [
        { id: 'UC-001', expected: { prefix: 'UC', number: '001' } },
        { id: 'NFR-PERF-001', expected: { prefix: 'NFR', category: 'PERF', number: '001' } },
        { id: 'US-042', expected: { prefix: 'US', number: '042' } },
        { id: 'F-005', expected: { prefix: 'F', number: '005' } },
        { id: 'INVALID', expected: null },
      ];

      for (const { id, expected } of testCases) {
        const parsed = extractor.parseId(id);
        expect(parsed, `parse failed for: ${id}`).toEqual(expected);
      }
    });
  });
});

describe('MatrixGenerator', () => {
  let generator: MatrixGenerator;

  beforeEach(() => {
    generator = new MatrixGenerator();
  });

  describe('generateMatrix', () => {
    it('should generate matrix from links', () => {
      const requirements = ['UC-001', 'UC-002'];
      const codeFiles = ['src/engine.ts'];
      const testFiles = ['test/engine.test.ts'];
      const links = new Map([
        ['UC-001', { code: ['src/engine.ts'], tests: ['test/engine.test.ts'] }],
        ['UC-002', { code: [], tests: [] }]
      ]);

      const matrix = generator.generateMatrix(requirements, codeFiles, testFiles, links);

      expect(matrix.requirements).toEqual(requirements);
      expect(matrix.code).toEqual(codeFiles);
      expect(matrix.tests).toEqual(testFiles);
      expect(matrix.links).toHaveLength(2);
    });

    it('should mark linked and unlinked cells correctly', () => {
      const requirements = ['UC-001'];
      const codeFiles = ['src/engine.ts'];
      const testFiles = ['test/engine.test.ts'];

      // Test with links
      const withLinks = new Map([
        ['UC-001', { code: ['src/engine.ts'], tests: ['test/engine.test.ts'] }]
      ]);
      const matrixLinked = generator.generateMatrix(requirements, codeFiles, testFiles, withLinks);
      const rowLinked = matrixLinked.links[0];

      const codeCellLinked = rowLinked.find(cell => cell.itemType === 'code');
      const testCellLinked = rowLinked.find(cell => cell.itemType === 'test');

      expect(codeCellLinked?.linked).toBe(true);
      expect(testCellLinked?.linked).toBe(true);

      // Test without links
      const withoutLinks = new Map([['UC-001', { code: [], tests: [] }]]);
      const matrixUnlinked = generator.generateMatrix(requirements, codeFiles, testFiles, withoutLinks);
      const rowUnlinked = matrixUnlinked.links[0];

      const codeCellUnlinked = rowUnlinked.find(cell => cell.itemType === 'code');
      const testCellUnlinked = rowUnlinked.find(cell => cell.itemType === 'test');

      expect(codeCellUnlinked?.linked).toBe(false);
      expect(testCellUnlinked?.linked).toBe(false);
    });
  });

  describe('export formats', () => {
    const createTestMatrix = (itemPath = 'src/engine.ts') => ({
      requirements: ['UC-001'],
      code: [itemPath],
      tests: ['test/engine.test.ts'],
      links: [
        [
          {
            requirementId: 'UC-001',
            itemPath,
            itemType: 'code' as const,
            linked: true,
            verified: true,
            confidence: 1.0
          },
          {
            requirementId: 'UC-001',
            itemPath: 'test/engine.test.ts',
            itemType: 'test' as const,
            linked: true,
            verified: true,
            confidence: 1.0
          }
        ]
      ]
    });

    it('should export to CSV with proper escaping and optional columns', () => {
      const matrix = createTestMatrix();
      const csv = generator.exportToCSV(matrix);

      expect(csv).toContain('Requirement');
      expect(csv).toContain('UC-001');
      expect(csv).toContain('src/engine.ts');
      expect(csv).toContain('test/engine.test.ts');

      // Test CSV escaping
      const matrixWithCommas = createTestMatrix('src/file,with,commas.ts');
      const csvEscaped = generator.exportToCSV(matrixWithCommas);
      expect(csvEscaped).toContain('"src/file,with,commas.ts"');

      // Test optional columns
      const csvWithVerification = generator.exportToCSV(matrix, { format: 'csv', includeVerification: true });
      expect(csvWithVerification).toContain('Verified');

      const csvWithConfidence = generator.exportToCSV(matrix, { format: 'csv', includeConfidence: true });
      expect(csvWithConfidence).toContain('Confidence');
    });

    it('should export to Markdown with coverage indicators', () => {
      const matrix = createTestMatrix();
      const md = generator.exportToMarkdown(matrix);

      expect(md).toContain('# Traceability Matrix');
      expect(md).toContain('**UC-001**');
      expect(md).toContain('src/engine.ts');
      expect(md).toContain('test/engine.test.ts');
      expect(md).toMatch(/[✅⚠️❌]/);

      // Test missing links
      const emptyMatrix = { requirements: ['UC-001'], code: [], tests: [], links: [[]] };
      const mdEmpty = generator.exportToMarkdown(emptyMatrix);
      expect(mdEmpty).toContain('*NONE*');
    });

    it('should export to HTML with CSS and entity escaping', () => {
      const matrix = createTestMatrix();
      const html = generator.exportToHTML(matrix);

      expect(html).toContain('<!DOCTYPE html>');
      expect(html).toContain('<title>Traceability Matrix</title>');
      expect(html).toContain('UC-001');
      expect(html).toContain('src/engine.ts');
      expect(html).toContain('<style>');
      expect(html).toContain('coverage-high');

      // Test HTML entity escaping
      const matrixWithScript = createTestMatrix('src/<script>.ts');
      const htmlEscaped = generator.exportToHTML(matrixWithScript);
      expect(htmlEscaped).toContain('&lt;script&gt;');
      expect(htmlEscaped).not.toContain('<script>');
    });

    it('should export to Excel (TSV)', () => {
      const matrix = createTestMatrix();
      const tsv = generator.exportToExcel(matrix);

      expect(tsv).toContain('\t');
      expect(tsv).not.toContain(',');
    });
  });
});

describe('TraceabilityChecker', () => {
  let sandbox: FilesystemSandbox;
  let checker: TraceabilityChecker;

  beforeEach(async () => {
    sandbox = new FilesystemSandbox();
    await sandbox.initialize();
    checker = new TraceabilityChecker(sandbox.getPath());
  });

  afterEach(async () => {
    await sandbox.cleanup();
  });

  describe('scanRequirements', () => {
    it('should scan requirements directory and extract all ID types', async () => {
      await sandbox.createDirectory('.aiwg/requirements');
      await sandbox.writeFile(
        '.aiwg/requirements/use-cases.md',
        `# UC-001: Validate AI-Generated Content

**Priority**: P0

Validate content against AI patterns.
`
      );
      await sandbox.writeFile(
        '.aiwg/requirements/nfrs.md',
        `# NFR-PERF-001: Context Loading Performance

**Priority**: P0

Context loading must complete in <5s for 95% of requests.
`
      );

      const requirements = await checker.scanRequirements();

      expect(requirements.size).toBeGreaterThan(0);
      expect(requirements.has('UC-001')).toBe(true);
      expect(requirements.has('NFR-PERF-001')).toBe(true);

      const uc001 = requirements.get('UC-001')!;
      expect(uc001.type).toBe('use-case');
      expect(uc001.priority).toBe('P0');

      const nfr = requirements.get('NFR-PERF-001')!;
      expect(nfr.type).toBe('nfr');
    });

    it('should process YAML files', async () => {
      await sandbox.createDirectory('.aiwg/requirements');
      await sandbox.writeFile(
        '.aiwg/requirements/requirements.yaml',
        `requirements:
  - id: UC-002
    title: Deploy Agent
    priority: P0
`
      );

      const requirements = await checker.scanRequirements();
      expect(requirements.has('UC-002')).toBe(true);
    });

    it('should handle missing requirements directory', async () => {
      const requirements = await checker.scanRequirements();
      expect(requirements.size).toBe(0);
    });
  });

  describe('scanCode', () => {
    it('should scan code directory and track line numbers', async () => {
      await sandbox.createDirectory('src');
      await sandbox.writeFile(
        'src/engine.ts',
        `Line 1
Line 2
// UC-001
Line 4
/**
 * ValidationEngine - Implements UC-001
 * NFR-PERF-001: Context loading <5s
 */
export class ValidationEngine {
  // Implementation
}
`
      );

      const codeRefs = await checker.scanCode();

      expect(codeRefs.size).toBeGreaterThan(0);
      const engineRef = Array.from(codeRefs.values())[0];
      expect(engineRef.requirementIds).toContain('UC-001');
      expect(engineRef.requirementIds).toContain('NFR-PERF-001');
      expect(engineRef.lineNumbers[0]).toBe(3);
    });

    it('should ignore test files and handle missing directory', async () => {
      await sandbox.createDirectory('src');
      await sandbox.writeFile('src/engine.test.ts', '// UC-001');

      const codeRefs = await checker.scanCode();
      expect(codeRefs.size).toBe(0);

      // Clean up and test missing directory
      await sandbox.cleanup();
      await sandbox.initialize();
      const checker2 = new TraceabilityChecker(sandbox.getPath());
      const codeRefs2 = await checker2.scanCode();
      expect(codeRefs2.size).toBe(0);
    });
  });

  describe('scanTests', () => {
    it('should scan test directory and extract test names', async () => {
      await sandbox.createDirectory('test');
      await sandbox.writeFile(
        'test/engine.test.ts',
        `describe('UC-001: AI Pattern Detection', () => {
  it('should detect banned phrases (NFR-ACC-001)', () => {
    // Test implementation
  });
});
it('should validate UC-001', () => {});
it('should handle errors', () => {});
`
      );

      const testRefs = await checker.scanTests();

      expect(testRefs.size).toBeGreaterThan(0);
      const engineTest = Array.from(testRefs.values())[0];
      expect(engineTest.requirementIds).toContain('UC-001');
      expect(engineTest.requirementIds).toContain('NFR-ACC-001');
      expect(engineTest.testNames).toContain('should validate UC-001');
      expect(engineTest.testNames).toContain('should handle errors');
    });

    it('should handle missing test directory', async () => {
      const testRefs = await checker.scanTests();
      expect(testRefs.size).toBe(0);
    });
  });

  describe('scanAll', () => {
    it('should scan all directories in parallel', async () => {
      // Create test structure
      await sandbox.createDirectory('.aiwg/requirements');
      await sandbox.createDirectory('src');
      await sandbox.createDirectory('test');

      await sandbox.writeFile('.aiwg/requirements/uc.md', '# UC-001');
      await sandbox.writeFile('src/engine.ts', '// UC-001');
      await sandbox.writeFile('test/engine.test.ts', '// UC-001');

      const result = await checker.scanAll();

      expect(result.requirements.size).toBeGreaterThan(0);
      expect(result.code.size).toBeGreaterThan(0);
      expect(result.tests.size).toBeGreaterThan(0);
      expect(result.scanTime).toBeGreaterThan(0);
    });

    it('should complete scan in <1min for 1000 files (NFR-TRACE-001)', async () => {
      // This is a performance test - skip in normal runs
      // To enable, set environment variable: RUN_PERF_TESTS=true
      if (!process.env.RUN_PERF_TESTS) {
        return;
      }

      // Create 1000 small files
      await sandbox.createDirectory('src');
      for (let i = 0; i < 1000; i++) {
        await sandbox.writeFile(`src/file${i}.ts`, `// UC-${String(i % 100).padStart(3, '0')}`);
      }

      const startTime = performance.now();
      await checker.scanAll();
      const scanTime = performance.now() - startTime;

      // Should complete in <60s (60000ms)
      expect(scanTime).toBeLessThan(60000);
    });
  });

  describe('buildTraceabilityLinks', () => {
    it('should build links and calculate coverage metrics', async () => {
      await sandbox.createDirectory('.aiwg/requirements');
      await sandbox.createDirectory('src');
      await sandbox.createDirectory('test');

      await sandbox.writeFile('.aiwg/requirements/uc.md', '# UC-001\n**Priority**: P0');
      await sandbox.writeFile('src/engine.ts', '// Implements UC-001');
      await sandbox.writeFile('test/engine.test.ts', '// Tests UC-001');

      await checker.scanAll();
      const links = await checker.buildTraceabilityLinks();

      expect(links.has('UC-001')).toBe(true);
      const link = links.get('UC-001')!;
      expect(link.linkedItems.length).toBeGreaterThan(0);
      expect(link.coverage.hasCode).toBe(true);
      expect(link.coverage.hasTests).toBe(true);
      expect(link.coverage.completeness).toBeGreaterThan(0);
    });

    it('should handle requirements without code', async () => {
      await sandbox.createDirectory('.aiwg/requirements');
      await sandbox.writeFile('.aiwg/requirements/uc.md', '# UC-999');

      await checker.scanAll();
      const links = await checker.buildTraceabilityLinks();
      const link = links.get('UC-999')!;

      expect(link.coverage.hasCode).toBe(false);
      expect(link.coverage.hasTests).toBe(false);
    });
  });

  describe('detectOrphans', () => {
    it('should detect orphaned requirements, code, and tests with severity', async () => {
      await sandbox.createDirectory('.aiwg/requirements');
      await sandbox.writeFile('.aiwg/requirements/p0.md', '# UC-001\n**Priority**: P0');
      await sandbox.writeFile('.aiwg/requirements/p1.md', '# UC-002\n**Priority**: P1');
      await sandbox.writeFile('.aiwg/requirements/p2.md', '# UC-003\n**Priority**: P2');

      await sandbox.createDirectory('src');
      await sandbox.writeFile('src/orphan.ts', 'export class Orphan {}');

      await sandbox.createDirectory('test');
      await sandbox.writeFile('test/orphan.test.ts', "it('orphaned test', () => {});");

      await checker.scanAll();
      const orphans = await checker.detectOrphans();

      expect(orphans.orphanedRequirements).toContain('UC-001');
      expect(orphans.orphanedCode.length).toBeGreaterThan(0);
      expect(orphans.orphanedTests.length).toBeGreaterThan(0);

      expect(orphans.severity.get('UC-001')).toBe('critical');
      expect(orphans.severity.get('UC-002')).toBe('warning');
      expect(orphans.severity.get('UC-003')).toBe('info');
    });
  });

  describe('calculateCoverage', () => {
    it('should calculate overall, by-type, and by-priority coverage', async () => {
      await sandbox.createDirectory('.aiwg/requirements');
      await sandbox.createDirectory('src');
      await sandbox.createDirectory('test');

      await sandbox.writeFile('.aiwg/requirements/uc1.md', '# UC-001');
      await sandbox.writeFile('.aiwg/requirements/uc2.md', '# UC-002');
      await sandbox.writeFile('.aiwg/requirements/nfr.md', '# NFR-PERF-001');
      await sandbox.writeFile('.aiwg/requirements/p0.md', '# UC-003\n**Priority**: P0');

      await sandbox.writeFile('src/engine.ts', '// UC-001\n// UC-003');
      await sandbox.writeFile('test/engine.test.ts', '// UC-001\n// UC-003');

      await checker.scanAll();
      const coverage = await checker.calculateCoverage();

      // 2 out of 4 requirements traced = 50%
      expect(coverage.percentage).toBe(50);
      expect(coverage.byType.has('use-case')).toBe(true);
      expect(coverage.byType.has('nfr')).toBe(true);
      expect(coverage.byPriority.has('P0')).toBe(true);
      expect(coverage.byPriority.get('P0')).toBe(100);
    });

    it('should identify gaps', async () => {
      await sandbox.createDirectory('.aiwg/requirements');
      await sandbox.writeFile('.aiwg/requirements/uc.md', '# UC-001');

      await checker.scanAll();
      const coverage = await checker.calculateCoverage();

      expect(coverage.gaps.requirementsWithoutCode).toContain('UC-001');
      expect(coverage.gaps.requirementsWithoutTests).toContain('UC-001');
    });
  });

  describe('generateMatrix', () => {
    it('should generate traceability matrix', async () => {
      await sandbox.createDirectory('.aiwg/requirements');
      await sandbox.createDirectory('src');
      await sandbox.createDirectory('test');

      await sandbox.writeFile('.aiwg/requirements/uc.md', '# UC-001');
      await sandbox.writeFile('src/engine.ts', '// UC-001');
      await sandbox.writeFile('test/engine.test.ts', '// UC-001');

      await checker.scanAll();
      const matrix = await checker.generateMatrix();

      expect(matrix.requirements).toContain('UC-001');
      expect(matrix.links.length).toBeGreaterThan(0);
    });

    it('should generate matrix in <30s for 1000 requirements (NFR-TRACE-05)', async () => {
      // Performance test - skip in normal runs
      if (!process.env.RUN_PERF_TESTS) {
        return;
      }

      // Create 1000 requirements
      await sandbox.createDirectory('.aiwg/requirements');
      for (let i = 0; i < 1000; i++) {
        await sandbox.writeFile(
          `.aiwg/requirements/uc${i}.md`,
          `# UC-${String(i).padStart(3, '0')}`
        );
      }

      await checker.scanAll();

      const startTime = performance.now();
      await checker.generateMatrix();
      const genTime = performance.now() - startTime;

      // Should complete in <30s (30000ms)
      expect(genTime).toBeLessThan(30000);
    });
  });

  describe('exportMatrix', () => {
    beforeEach(async () => {
      await sandbox.createDirectory('.aiwg/requirements');
      await sandbox.createDirectory('src');
      await sandbox.createDirectory('test');

      await sandbox.writeFile('.aiwg/requirements/uc.md', '# UC-001');
      await sandbox.writeFile('src/engine.ts', '// UC-001');
      await sandbox.writeFile('test/engine.test.ts', '// UC-001');

      await checker.scanAll();
    });

    it('should export matrix to all formats', async () => {
      const formats = [
        { format: 'csv' as const, expectedContent: 'UC-001' },
        { format: 'markdown' as const, expectedContent: '# Traceability Matrix' },
        { format: 'html' as const, expectedContent: '<!DOCTYPE html>' },
        { format: 'excel' as const, expectedContent: '\t' },
      ];

      for (const { format, expectedContent } of formats) {
        const filePath = await checker.exportMatrix(format);
        const exists = await sandbox.fileExists(filePath.replace(sandbox.getPath() + '/', ''));
        expect(exists, `file should exist for: ${format}`).toBe(true);

        const content = await sandbox.readFile(filePath.replace(sandbox.getPath() + '/', ''));
        expect(content, `wrong content for: ${format}`).toContain(expectedContent);
      }
    });
  });

  describe('generateReport', () => {
    it('should generate comprehensive report', async () => {
      await sandbox.createDirectory('.aiwg/requirements');
      await sandbox.createDirectory('src');

      await sandbox.writeFile('.aiwg/requirements/uc1.md', '# UC-001');
      await sandbox.writeFile('.aiwg/requirements/uc2.md', '# UC-002');
      await sandbox.writeFile('src/engine.ts', '// UC-001');

      await checker.scanAll();
      const report = await checker.generateReport();

      expect(report.totalRequirements).toBe(2);
      expect(report.orphanedRequirements.length).toBeGreaterThan(0);
      expect(report.gapsByRequirement.size).toBeGreaterThan(0);
    });
  });

  describe('exportReport', () => {
    beforeEach(async () => {
      await sandbox.createDirectory('.aiwg/requirements');
      await sandbox.writeFile('.aiwg/requirements/uc.md', '# UC-001');
      await checker.scanAll();
    });

    it('should export report to all formats', async () => {
      const formats = [
        { format: 'markdown' as const },
        { format: 'json' as const },
        { format: 'html' as const },
      ];

      for (const { format } of formats) {
        const filePath = await checker.exportReport(format);
        const exists = await sandbox.fileExists(filePath.replace(sandbox.getPath() + '/', ''));
        expect(exists, `file should exist for: ${format}`).toBe(true);

        if (format === 'json') {
          const content = await sandbox.readFile(filePath.replace(sandbox.getPath() + '/', ''));
          const json = JSON.parse(content);
          expect(json.totalRequirements).toBeGreaterThan(0);
        }
      }
    });
  });

  describe('validateTraceability', () => {
    it('should validate against threshold', async () => {
      await sandbox.createDirectory('.aiwg/requirements');
      await sandbox.createDirectory('src');
      await sandbox.createDirectory('test');

      await sandbox.writeFile('.aiwg/requirements/uc.md', '# UC-001');
      await sandbox.writeFile('src/engine.ts', '// UC-001');
      await sandbox.writeFile('test/engine.test.ts', '// UC-001');

      await checker.scanAll();
      const result = await checker.validateTraceability(0.8); // 80% threshold

      expect(result.passed).toBe(true);
      expect(result.coverage).toBe(100);
    });

    it('should fail when below threshold', async () => {
      await sandbox.createDirectory('.aiwg/requirements');
      await sandbox.writeFile('.aiwg/requirements/uc.md', '# UC-001');

      await checker.scanAll();
      const result = await checker.validateTraceability(0.5);

      expect(result.passed).toBe(false);
      expect(result.issues.length).toBeGreaterThan(0);
    });
  });

  describe('checkConstructionGate', () => {
    it('should pass when all P0 requirements traced', async () => {
      await sandbox.createDirectory('.aiwg/requirements');
      await sandbox.createDirectory('src');
      await sandbox.createDirectory('test');

      await sandbox.writeFile('.aiwg/requirements/p0.md', '# UC-001\n**Priority**: P0');
      await sandbox.writeFile('src/engine.ts', '// UC-001');
      await sandbox.writeFile('test/engine.test.ts', '// UC-001');

      await checker.scanAll();
      const result = await checker.checkConstructionGate();

      expect(result.passed).toBe(true);
      expect(result.p0Coverage).toBe(100);
    });

    it('should fail when P0 requirements not traced and warn for low P1 coverage', async () => {
      await sandbox.createDirectory('.aiwg/requirements');
      await sandbox.writeFile('.aiwg/requirements/p0.md', '# UC-001\n**Priority**: P0');
      await sandbox.writeFile('.aiwg/requirements/p1.md', '# UC-002\n**Priority**: P1');

      await checker.scanAll();
      const result = await checker.checkConstructionGate();

      expect(result.passed).toBe(false);
      expect(result.issues.length).toBeGreaterThan(0);
      expect(result.warnings.length).toBeGreaterThan(0);
    });
  });

  describe('link management', () => {
    it('should add, remove, and update links', async () => {
      await sandbox.createDirectory('.aiwg/requirements');
      await sandbox.createDirectory('src');
      await sandbox.writeFile('.aiwg/requirements/uc.md', '# UC-001');
      await sandbox.writeFile('src/engine.ts', '// UC-001');
      await checker.scanAll();

      // Add link
      await checker.addLink('UC-001', {
        type: 'code',
        path: 'src/new-file.ts',
        verified: true,
        confidence: 1.0
      });

      await checker.addLink('UC-001', {
        type: 'test',
        path: 'test/new-test.test.ts',
        verified: true,
        confidence: 1.0
      });

      let links = await checker.buildTraceabilityLinks();
      let link = links.get('UC-001')!;

      expect(link.linkedItems.some(item => item.path === 'src/new-file.ts')).toBe(true);
      expect(link.linkedItems.some(item => item.path === 'test/new-test.test.ts')).toBe(true);

      // Remove link
      await checker.removeLink('UC-001', sandbox.getPath() + '/src/engine.ts');
      links = await checker.buildTraceabilityLinks();
      link = links.get('UC-001')!;
      expect(link.linkedItems.some(item => item.path.includes('engine.ts'))).toBe(false);

      // Update link
      const oldPath = sandbox.getPath() + '/src/new-file.ts';
      const newPath = sandbox.getPath() + '/src/updated-file.ts';
      await checker.updateLink('UC-001', oldPath, {
        type: 'code',
        path: newPath,
        verified: true,
        confidence: 0.9
      });

      links = await checker.buildTraceabilityLinks();
      link = links.get('UC-001')!;
      expect(link.linkedItems.some(item => item.path === newPath)).toBe(true);
    });

    it('should throw error for non-existent requirement', async () => {
      await checker.scanAll();

      await expect(
        checker.addLink('UC-999', {
          type: 'code',
          path: 'src/file.ts',
          verified: true,
          confidence: 1.0
        })
      ).rejects.toThrow();
    });
  });
});
