import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { readPersonaContractBlock } from './persona-contract.js';

/**
 * Persona-contract launch injection (A3b). Asserts the override-aware resolver
 * is wired so a customized persona in roles.local/ wins at launch (AC-NS-7), and
 * that any miss (unset/empty/unknown class, missing file) no-ops silently —
 * never throws — mirroring readFleetCommsBlock's tolerant contract.
 */

const BASELINE_CODER = `# Coder — fleet role definition

The **coder** persona (\`class: coder\`, \`domain: engineering\`).

## Mandate

BASELINE-MANDATE: implement the assigned lane.
`;

const OVERRIDE_CODER = `# Coder — fleet role definition (override)

The **coder** persona (\`class: coder\`).

## Mandate

OVERRIDE-MANDATE: implement the assigned lane, the user's way.
`;

function makeHome(): string {
  const root = mkdtempSync(join(tmpdir(), 'mosaic-persona-'));
  return join(root, 'mosaic-home');
}

function seedBaseline(home: string, klass: string, body: string): void {
  const dir = join(home, 'fleet', 'roles');
  mkdirSync(dir, { recursive: true });
  writeFileSync(join(dir, `${klass}.md`), body);
}

function seedOverride(home: string, klass: string, body: string): void {
  const dir = join(home, 'fleet', 'roles.local');
  mkdirSync(dir, { recursive: true });
  writeFileSync(join(dir, `${klass}.md`), body);
}

describe('readPersonaContractBlock — launch-time persona injection (A3b)', () => {
  let home: string;

  beforeEach(() => {
    home = makeHome();
  });

  afterEach(() => {
    // root is the parent of mosaic-home
    rmSync(join(home, '..'), { recursive: true, force: true });
  });

  it('injects the baseline persona when the class has a fleet/roles/<class>.md', () => {
    seedBaseline(home, 'coder', BASELINE_CODER);
    const block = readPersonaContractBlock(home, 'coder');
    expect(block).toContain('# Persona Contract (coder)');
    expect(block).toContain('BASELINE-MANDATE');
    expect(block).toContain('baseline `fleet/roles/` layer');
  });

  it('OVERRIDE WINS: roles.local/<class>.md content is injected over the baseline (AC-NS-7)', () => {
    seedBaseline(home, 'coder', BASELINE_CODER);
    seedOverride(home, 'coder', OVERRIDE_CODER);
    const block = readPersonaContractBlock(home, 'coder');
    expect(block).toContain('# Persona Contract (coder)');
    expect(block).toContain('OVERRIDE-MANDATE'); // override body present
    expect(block).not.toContain('BASELINE-MANDATE'); // baseline NOT used
    expect(block).toContain('roles.local'); // layer note names the override layer
  });

  it('injects an override-only (user-added) persona with no baseline at all', () => {
    seedOverride(home, 'reviewer', '# Reviewer\n\n(`class: reviewer`)\n\nCUSTOM-ROLE.\n');
    const block = readPersonaContractBlock(home, 'reviewer');
    expect(block).toContain('# Persona Contract (reviewer)');
    expect(block).toContain('CUSTOM-ROLE');
  });

  it('no-ops (empty string) when the class is undefined', () => {
    seedBaseline(home, 'coder', BASELINE_CODER);
    expect(readPersonaContractBlock(home, undefined)).toBe('');
  });

  it('no-ops (empty string) when the class is empty/whitespace', () => {
    seedBaseline(home, 'coder', BASELINE_CODER);
    expect(readPersonaContractBlock(home, '')).toBe('');
    expect(readPersonaContractBlock(home, '   ')).toBe('');
  });

  it('no-ops (empty string) for an unknown class with no role file', () => {
    seedBaseline(home, 'coder', BASELINE_CODER);
    expect(readPersonaContractBlock(home, 'nonexistent')).toBe('');
  });

  it('no-ops (empty string, no throw) when no roles directories exist at all', () => {
    expect(() => readPersonaContractBlock(home, 'coder')).not.toThrow();
    expect(readPersonaContractBlock(home, 'coder')).toBe('');
  });
});
