import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { describe, it, expect, beforeEach, afterEach } from 'vitest';

import {
  buildPersonaBrief,
  writePersonaBrief,
  personaResultPath,
  synthesisResultPath,
  generateBoardTasks,
  synthesizeReviews,
} from '../src/board-tasks.js';
import type { BoardPersona, PersonaReview } from '../src/types.js';

const testPersonas: BoardPersona[] = [
  { name: 'CEO', slug: 'ceo', description: 'The CEO sets direction.', path: 'agents/board/ceo.md' },
  {
    name: 'CTO',
    slug: 'cto',
    description: 'The CTO evaluates feasibility.',
    path: 'agents/board/cto.md',
  },
];

describe('buildPersonaBrief', () => {
  it('includes persona name and description', () => {
    const brief = buildPersonaBrief('Build feature X', testPersonas[0]!);
    expect(brief).toContain('# Board Evaluation: CEO');
    expect(brief).toContain('The CEO sets direction.');
    expect(brief).toContain('Build feature X');
    expect(brief).toContain('"persona": "CEO"');
  });
});

describe('writePersonaBrief', () => {
  let tmpDir: string;

  beforeEach(() => {
    tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'forge-board-'));
  });

  afterEach(() => {
    fs.rmSync(tmpDir, { recursive: true, force: true });
  });

  it('writes brief file to disk', () => {
    const briefPath = writePersonaBrief(tmpDir, 'BOARD', testPersonas[0]!, 'Test brief');
    expect(fs.existsSync(briefPath)).toBe(true);
    const content = fs.readFileSync(briefPath, 'utf-8');
    expect(content).toContain('Board Evaluation: CEO');
  });
});

describe('personaResultPath', () => {
  it('builds correct path', () => {
    const p = personaResultPath('/run/abc', 'BOARD-ceo');
    expect(p).toContain('01-board/results/BOARD-ceo.board.json');
  });
});

describe('synthesisResultPath', () => {
  it('builds correct path', () => {
    const p = synthesisResultPath('/run/abc', 'BOARD-SYNTHESIS');
    expect(p).toContain('01-board/results/BOARD-SYNTHESIS.board.json');
  });
});

describe('generateBoardTasks', () => {
  let tmpDir: string;

  beforeEach(() => {
    tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'forge-board-tasks-'));
  });

  afterEach(() => {
    fs.rmSync(tmpDir, { recursive: true, force: true });
  });

  it('generates one task per persona plus synthesis', () => {
    const tasks = generateBoardTasks('Test brief', testPersonas, tmpDir);
    expect(tasks).toHaveLength(3); // 2 personas + 1 synthesis
  });

  it('persona tasks have no dependsOn', () => {
    const tasks = generateBoardTasks('Test brief', testPersonas, tmpDir);
    expect(tasks[0]!.dependsOn).toBeUndefined();
    expect(tasks[1]!.dependsOn).toBeUndefined();
  });

  it('synthesis task depends on all persona tasks', () => {
    const tasks = generateBoardTasks('Test brief', testPersonas, tmpDir);
    const synthesis = tasks[tasks.length - 1]!;
    expect(synthesis.id).toBe('BOARD-SYNTHESIS');
    expect(synthesis.dependsOn).toEqual(['BOARD-ceo', 'BOARD-cto']);
    expect(synthesis.dependsOnPolicy).toBe('all_terminal');
  });

  it('persona tasks have correct metadata', () => {
    const tasks = generateBoardTasks('Test brief', testPersonas, tmpDir);
    expect(tasks[0]!.metadata['personaName']).toBe('CEO');
    expect(tasks[0]!.metadata['personaSlug']).toBe('ceo');
  });

  it('uses custom base task ID', () => {
    const tasks = generateBoardTasks('Brief', testPersonas, tmpDir, 'CUSTOM');
    expect(tasks[0]!.id).toBe('CUSTOM-ceo');
    expect(tasks[tasks.length - 1]!.id).toBe('CUSTOM-SYNTHESIS');
  });

  it('writes persona brief files to disk', () => {
    generateBoardTasks('Test brief', testPersonas, tmpDir);
    const briefDir = path.join(tmpDir, '01-board', 'briefs');
    expect(fs.existsSync(briefDir)).toBe(true);
    const files = fs.readdirSync(briefDir);
    expect(files).toHaveLength(2);
  });
});

describe('synthesizeReviews', () => {
  const makeReview = (
    persona: string,
    verdict: PersonaReview['verdict'],
    confidence: number,
  ): PersonaReview => ({
    persona,
    verdict,
    confidence,
    concerns: [`${persona} concern`],
    recommendations: [`${persona} rec`],
    keyRisks: [`${persona} risk`],
  });

  it('returns approve when all approve', () => {
    const result = synthesizeReviews([
      makeReview('CEO', 'approve', 0.8),
      makeReview('CTO', 'approve', 0.9),
    ]);
    expect(result.verdict).toBe('approve');
    expect(result.confidence).toBe(0.85);
    expect(result.persona).toBe('Board Synthesis');
  });

  it('returns reject when any reject', () => {
    const result = synthesizeReviews([
      makeReview('CEO', 'approve', 0.8),
      makeReview('CTO', 'reject', 0.7),
    ]);
    expect(result.verdict).toBe('reject');
  });

  it('returns conditional when any conditional (no reject)', () => {
    const result = synthesizeReviews([
      makeReview('CEO', 'approve', 0.8),
      makeReview('CTO', 'conditional', 0.6),
    ]);
    expect(result.verdict).toBe('conditional');
  });

  it('merges and deduplicates concerns', () => {
    const reviews = [makeReview('CEO', 'approve', 0.8), makeReview('CTO', 'approve', 0.9)];
    const result = synthesizeReviews(reviews);
    expect(result.concerns).toEqual(['CEO concern', 'CTO concern']);
    expect(result.recommendations).toEqual(['CEO rec', 'CTO rec']);
  });

  it('deduplicates identical items', () => {
    const r1: PersonaReview = {
      persona: 'CEO',
      verdict: 'approve',
      confidence: 0.8,
      concerns: ['shared concern'],
      recommendations: [],
      keyRisks: [],
    };
    const r2: PersonaReview = {
      persona: 'CTO',
      verdict: 'approve',
      confidence: 0.8,
      concerns: ['shared concern'],
      recommendations: [],
      keyRisks: [],
    };
    const result = synthesizeReviews([r1, r2]);
    expect(result.concerns).toEqual(['shared concern']);
  });

  it('includes original reviews', () => {
    const reviews = [makeReview('CEO', 'approve', 0.8)];
    const result = synthesizeReviews(reviews);
    expect(result.reviews).toEqual(reviews);
  });

  it('handles empty reviews', () => {
    const result = synthesizeReviews([]);
    expect(result.verdict).toBe('approve');
    expect(result.confidence).toBe(0);
  });
});
