import { describe, it, expect } from 'vitest';
import { findConflicts } from '../collision-detector.js';
import type { ConflictEntry, ConflictIndexData } from '../collision-detector.js';

function entry(worktreeId: string, agent: string, start?: number, end?: number): ConflictEntry {
  return {
    worktreeId,
    agent,
    lineRange: start !== undefined && end !== undefined ? { start, end } : undefined,
    status: 'pending' as const,
    timestamp: 1000,
  };
}

function index(entries: Array<[string, ConflictEntry[]]>): ConflictIndexData {
  return new Map(entries.map(([path, es]) => [path, new Set(es)] as const));
}

describe('findConflicts', () => {
  it('returns empty when no files in index', () => {
    const result = findConflicts(['src/a.ts'], 'wt-1', new Map(), new Map());
    expect(result).toEqual([]);
  });

  it('returns empty when only own worktree has the file', () => {
    const idx = index([['src/a.ts', [entry('wt-1', 'agent-a', 1, 10)]]]);
    const result = findConflicts(['src/a.ts'], 'wt-1', new Map(), idx);
    expect(result).toEqual([]);
  });

  it('detects same_file conflict from another worktree', () => {
    const idx = index([['src/a.ts', [entry('wt-2', 'agent-b', 5, 15)]]]);
    const result = findConflicts(['src/a.ts'], 'wt-1', new Map(), idx);
    expect(result).toHaveLength(1);
    expect(result[0]!.filePath).toBe('src/a.ts');
    expect(result[0]!.worktrees).toEqual(['wt-2']);
    expect(result[0]!.agents).toEqual(['agent-b']);
  });

  it('reports same_line severity when ranges overlap', () => {
    const idx = index([['src/a.ts', [entry('wt-2', 'agent-b', 10, 20)]]]);
    const ranges = new Map([['src/a.ts', { start: 15, end: 25 }]]);
    const result = findConflicts(['src/a.ts'], 'wt-1', ranges, idx);
    expect(result[0]!.severity).toBe('same_line');
  });

  it('reports different_area severity when ranges are far apart', () => {
    const idx = index([['src/a.ts', [entry('wt-2', 'agent-b', 1, 10)]]]);
    const ranges = new Map([['src/a.ts', { start: 100, end: 200 }]]);
    const result = findConflicts(['src/a.ts'], 'wt-1', ranges, idx);
    expect(result[0]!.severity).toBe('different_area');
  });

  it('reports adjacent_line severity when ranges are 3 lines apart', () => {
    const idx = index([['src/a.ts', [entry('wt-2', 'agent-b', 10, 15)]]]);
    const ranges = new Map([['src/a.ts', { start: 19, end: 25 }]]);
    const result = findConflicts(['src/a.ts'], 'wt-1', ranges, idx);
    expect(result[0]!.severity).toBe('adjacent_line');
  });

  it('returns entry for each conflicting file', () => {
    const idx = index([
      ['src/a.ts', [entry('wt-2', 'agent-b', 1, 10)]],
      ['src/b.ts', [entry('wt-3', 'agent-c', 1, 10)]],
    ]);
    const result = findConflicts(['src/a.ts', 'src/b.ts', 'src/c.ts'], 'wt-1', new Map(), idx);
    expect(result).toHaveLength(2);
    expect(result.map((v) => v.filePath).sort()).toEqual(['src/a.ts', 'src/b.ts']);
  });

  it('excludes entries from the same worktree', () => {
    const idx = index([['src/a.ts', [entry('wt-1', 'agent-a', 1, 10), entry('wt-2', 'agent-b', 5, 15)]]]);
    const result = findConflicts(['src/a.ts'], 'wt-1', new Map(), idx);
    expect(result).toHaveLength(1);
    expect(result[0]!.worktrees).toEqual(['wt-2']);
  });

  it('deduplicates worktree IDs in verdict', () => {
    const idx = index([['src/a.ts', [entry('wt-2', 'agent-b', 1, 5), entry('wt-2', 'agent-b', 10, 15)]]]);
    const result = findConflicts(['src/a.ts'], 'wt-1', new Map(), idx);
    expect(result[0]!.worktrees).toEqual(['wt-2']);
  });

  it('reports same_line when no lineRange on either side (create/delete conflates)', () => {
    const idx = index([['src/a.ts', [entry('wt-2', 'agent-b')]]]);
    const result = findConflicts(['src/a.ts'], 'wt-1', new Map(), idx);
    expect(result).toHaveLength(1);
    expect(result[0]!.severity).toBe('different_area');
  });
});
