import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { mkdtempSync, rmSync } from 'fs';
import { join } from 'path';
import { tmpdir } from 'os';
import Database from 'better-sqlite3';
import { Repository, MISSION_BRIEF_FIELDS } from './repository.js';

let tempDir = '';
let dbPath = '';
let repo: Repository;

async function seedTask(): Promise<number> {
  const created = await repo.createLocalTask({
    title: 'seed',
    body: 'do the thing',
    pieceName: 'auto',
    ownerId: 'owner-1',
    visibility: 'private',
  });
  return created.id;
}

beforeEach(() => {
  tempDir = mkdtempSync(join(tmpdir(), 'maestro-mb-'));
  dbPath = join(tempDir, 'db.sqlite');
  repo = new Repository(dbPath);
});

afterEach(() => {
  repo.close();
  rmSync(tempDir, { recursive: true, force: true });
});

describe('MissionBrief schema extension', () => {
  it('exposes the ordered field list with legacy fields first', () => {
    expect(MISSION_BRIEF_FIELDS.slice(0, 4)).toEqual(['goal', 'done', 'open', 'clarifications']);
    expect(MISSION_BRIEF_FIELDS).toContain('user_constraints');
    expect(MISSION_BRIEF_FIELDS).toContain('decisions');
    expect(MISSION_BRIEF_FIELDS).toContain('current_focus');
  });

  it('loads legacy JSON with only the four original fields (backward compat)', async () => {
    const id = await seedTask();
    // Simulate a brief stored by an older build: only legacy keys present.
    const raw = new Database(dbPath);
    raw.prepare('UPDATE local_tasks SET mission_brief = ? WHERE id = ?')
      .run(JSON.stringify({ goal: 'G', done: 'D', open: 'O', clarifications: 'C' }), id);
    raw.close();

    const brief = repo.getMissionBriefSync(id);
    expect(brief).not.toBeNull();
    expect(brief!.goal).toBe('G');
    expect(brief!.clarifications).toBe('C');
    // New fields default to empty string, never crash.
    expect(brief!.user_constraints ?? '').toBe('');
    expect(brief!.decisions ?? '').toBe('');
    expect(brief!.current_focus ?? '').toBe('');
  });

  it('persists and reads back the new fields', async () => {
    const id = await seedTask();
    repo.updateMissionBriefSync(id, {
      goal: 'ship it',
      user_constraints: 'keep auth flow unchanged',
      decisions: 'extend brief instead of new store',
      current_focus: 'wiring the tools',
    });
    const brief = repo.getMissionBriefSync(id);
    expect(brief!.goal).toBe('ship it');
    expect(brief!.user_constraints).toBe('keep auth flow unchanged');
    expect(brief!.decisions).toBe('extend brief instead of new store');
    expect(brief!.current_focus).toBe('wiring the tools');
  });

  it('partial update of one new field does not clobber the others', async () => {
    const id = await seedTask();
    repo.updateMissionBriefSync(id, { user_constraints: 'A', decisions: 'B', current_focus: 'C' });
    repo.updateMissionBriefSync(id, { decisions: 'B2' });
    const brief = repo.getMissionBriefSync(id);
    expect(brief!.user_constraints).toBe('A');
    expect(brief!.decisions).toBe('B2');
    expect(brief!.current_focus).toBe('C');
  });

  it('treats a brief with only new fields as non-empty (allEmpty across all fields)', async () => {
    const id = await seedTask();
    const merged = repo.updateMissionBriefSync(id, { decisions: 'only a decision' });
    expect(merged).not.toBeNull();
    expect(repo.getMissionBriefSync(id)!.decisions).toBe('only a decision');
  });

  it('derives title from goal only, unaffected by new fields', async () => {
    const id = await seedTask();
    repo.updateMissionBriefSync(id, { current_focus: 'focus text', decisions: 'd' });
    const t1 = await repo.getLocalTask(id);
    // Setting only new fields must not derive a title from them.
    repo.updateMissionBriefSync(id, { goal: 'Real user goal here' });
    const t2 = await repo.getLocalTask(id);
    expect(t2!.title).not.toBe(t1!.title);
    expect(t2!.title.length).toBeGreaterThan(0);
  });
});
