import test from 'node:test';
import assert from 'node:assert/strict';
import os from 'os';
import path from 'path';

import { behaviorTracker } from './BehaviorTracker.js';
import { narrativePhaseTracker } from './NarrativePhaseTracker.js';
import { hiddenHookTracker } from './HiddenHookTracker.js';
import { saveState } from './SaveState.js';
import { endingEvaluator } from './EndingEvaluator.js';
import { eventBus } from '../lib/eventBus.js';

async function isolateSaveState(testId) {
  await saveState._writeQueue.catch(() => {});
  process.env.SAVE_DIR = path.join(os.tmpdir(), `sc-ee-test-${testId}-${Date.now()}`);
  saveState._savePath = null;
  saveState._state = null;
  saveState._writeQueue = Promise.resolve();
}

function resetAll({
  curiosity = 50,
  obedience = 50,
  risk = 50,
  suspicion = 0,
  trust = 50,
  phase = 'normal_work',
  hooks = []
} = {}) {
  behaviorTracker.initialize({ behavior: { curiosity, obedience, risk, suspicion } });
  narrativePhaseTracker.initialize({ narrative_phase: phase });
  hiddenHookTracker.initialize({ hidden_hooks_discovered: hooks });
  saveState.set({ trust });
}

test('default state returns undetermined', async () => {
  await isolateSaveState('default');
  resetAll();

  assert.deepEqual(endingEvaluator.evaluate(), { active: 'undetermined', candidates: [] });
});

test('exposure ending', async () => {
  await isolateSaveState('exposure');
  resetAll({ curiosity: 70, phase: 'investigation', hooks: ['h1', 'h2'] });

  const result = endingEvaluator.evaluate();

  assert.equal(result.active, 'exposure');
  assert.ok(result.candidates.includes('exposure'));
});

test('chaos ending', async () => {
  await isolateSaveState('chaos');
  resetAll({ risk: 70, trust: 30 });

  assert.equal(endingEvaluator.evaluate().active, 'chaos');
});

test('corporate_loop ending', async () => {
  await isolateSaveState('corporate-loop');
  resetAll({ obedience: 70, curiosity: 35, trust: 70 });

  assert.equal(endingEvaluator.evaluate().active, 'corporate_loop');
});

test('burnout ending', async () => {
  await isolateSaveState('burnout');
  resetAll({ curiosity: 30, obedience: 35 });

  assert.equal(endingEvaluator.evaluate().active, 'burnout');
});

test('exposure takes priority over chaos', async () => {
  await isolateSaveState('exposure-priority');
  resetAll({ curiosity: 70, risk: 70, trust: 30, phase: 'investigation', hooks: ['h1', 'h2'] });

  const result = endingEvaluator.evaluate();

  assert.equal(result.active, 'exposure');
  assert.ok(result.candidates.includes('exposure'));
  assert.ok(result.candidates.includes('chaos'));
});

test('checkTrigger at resolution emits ending:triggered', async () => {
  await isolateSaveState('trigger-resolution');
  resetAll({ curiosity: 70, phase: 'resolution', hooks: ['h1', 'h2'] });

  let event;
  eventBus.once('ending:triggered', (payload) => {
    event = payload;
  });

  endingEvaluator.checkTrigger();

  assert.deepEqual(event, { ending: 'exposure' });
});

test('checkTrigger NOT at resolution does NOT emit', async () => {
  await isolateSaveState('trigger-not-resolution');
  resetAll({ curiosity: 70, phase: 'investigation', hooks: ['h1', 'h2'] });

  let called = false;
  function fn() {
    called = true;
  }

  eventBus.on('ending:triggered', fn);
  try {
    endingEvaluator.checkTrigger();
  } finally {
    eventBus.removeListener('ending:triggered', fn);
  }

  assert.equal(called, false);
});
