/*
 * Priority Forge — V4.4 cross-forge ML continuity.
 *
 * When a task is a SharedTask projection (has `sharedTaskId`), the ML records it
 * generates must carry that stable cross-forge key. Otherwise a release→reclaim
 * cycle (which mints a fresh local id) orphans every prior record. This pins the
 * stamping at each log site + the exportTrainingData fallback join.
 *
 * Follows the live-data pattern of lifecycle-synthesis.test.ts; run isolated:
 *   npx vitest run tests/shared-task-ml-continuity.test.ts
 */

import { describe, it, expect, afterEach } from 'vitest';
import { JsonStorage } from '../src/storage/jsonStorage';
import type {
  TaskSelectionEvent,
  PriorityChangeEvent,
} from '../src/types/schema';

const instances: JsonStorage[] = [];
const tasksToCleanup: Array<{ storage: JsonStorage; id: string }> = [];

afterEach(async () => {
  for (const { storage, id } of tasksToCleanup.splice(0)) {
    try {
      await storage.deleteTask(id);
    } catch {
      /* fine — task may already be gone */
    }
  }
  for (const s of instances.splice(0)) {
    (s as unknown as { stopFileWatcher?: () => void }).stopFileWatcher?.();
  }
});

function makeStorage(): JsonStorage {
  const s = new JsonStorage();
  instances.push(s);
  return s;
}

function globalML(s: JsonStorage) {
  return (s as unknown as {
    globalML: {
      taskSelectionEvents: TaskSelectionEvent[];
      priorityChangeEvents: PriorityChangeEvent[];
    };
  }).globalML;
}

describe('V4.4 — ML records carry sharedTaskId for projection tasks', () => {
  it('selection, priority-change, and completion records all stamp sharedTaskId', async () => {
    const s = makeStorage();
    const stId = `st_test_${Date.now()}`;
    const task = await s.createTask({
      task: 'V4.4 continuity probe',
      priority: 'P3',
      project: 'priority-forge',
      effort: 'low',
      sharedTaskId: stId,
    });
    tasksToCleanup.push({ storage: s, id: task.id });
    expect(task.sharedTaskId).toBe(stId);

    // Priority change → PriorityChangeEvent
    await s.updateTask(task.id, { priority: 'P0' });
    const pc = globalML(s).priorityChangeEvents.filter((e) => e.taskId === task.id);
    expect(pc.length).toBeGreaterThan(0);
    expect(pc[pc.length - 1].sharedTaskId).toBe(stId);

    // in_progress → TaskSelectionEvent (auto-logged)
    await s.updateTask(task.id, { status: 'in_progress' });
    const sel = globalML(s).taskSelectionEvents.filter((e) => e.selectedTaskId === task.id);
    expect(sel.length).toBeGreaterThan(0);
    expect(sel[sel.length - 1].sharedTaskId).toBe(stId);

    // complete → TaskCompletionRecord (returned)
    const record = await s.completeTask(task.id, 'completed');
    expect(record).not.toBeNull();
    expect(record!.sharedTaskId).toBe(stId);
  });

  it('a task with no sharedTaskId leaves the field undefined (no spurious stamping)', async () => {
    const s = makeStorage();
    const task = await s.createTask({
      task: 'V4.4 non-projection probe',
      priority: 'P3',
      project: 'priority-forge',
      effort: 'low',
    });
    tasksToCleanup.push({ storage: s, id: task.id });

    const record = await s.completeTask(task.id, 'completed');
    expect(record).not.toBeNull();
    expect(record!.sharedTaskId).toBeUndefined();
  });
});
