// Undo roundtrip: every op that returns log.undo must restore prior state.
// Apply op → patch → apply undo → assert deepEqual original.

const shared = require('@ttrpg/shared');
const {
  makeParticipant, startEncounter, nextTurn, togglePause,
  addParticipant, removeParticipant, toggleParticipantActive,
  applyHpChange, toggleCondition, reorderParticipants, endEncounter,
} = shared;

function p(id, init, extra = {}) {
  return makeParticipant({
    id, name: id, type: 'monster',
    initiative: init, maxHp: 100, currentHp: 100,
    ...extra,
  });
}
function enc(ps) {
  return { name:'t', participants:ps, isStarted:false, isPaused:false,
    round:0, currentTurnParticipantId:null, turnOrderIds:[] };
}
const snap = (e) => JSON.parse(JSON.stringify(e));

describe('undo roundtrip', () => {
  test('startEncounter undo restores pre-start', () => {
    const before = enc([p('a',10),p('b',20)]);
    const r = startEncounter(before);
    expect(r.log.undo).toBeTruthy();
    // undo restores isStarted/isPaused/round/current/turnOrderIds.
    // participants[] may be reordered (1-list sort on start) — undo snapshot
    // captures turn-state fields, not participant order.
    const after = { ...before, ...r.patch, ...r.log.undo };
    expect(after.isStarted).toBe(before.isStarted);
    expect(after.isPaused).toBe(before.isPaused);
    expect(after.round).toBe(before.round);
    expect(after.currentTurnParticipantId).toBe(before.currentTurnParticipantId);
    expect(after.turnOrderIds).toEqual(before.turnOrderIds);
  });

  test('nextTurn undo restores prior currentTurn/round', () => {
    let e = enc([p('a',10),p('b',20),p('c',5)]);
    e = { ...e, ...startEncounter(e).patch };
    const before = snap(e);
    const r = nextTurn(e);
    expect(r.log.undo).toBeTruthy();
    const after = { ...e, ...r.patch, ...r.log.undo };
    expect(snap(after)).toEqual(before);
  });

  test('togglePause undo restores prior paused state', () => {
    let e = enc([p('a',10),p('b',20)]);
    e = { ...e, ...startEncounter(e).patch };
    const before = snap(e);
    const r = togglePause(e);
    expect(r.log.undo).toBeTruthy();
    const after = { ...e, ...r.patch, ...r.log.undo };
    expect(snap(after)).toEqual(before);
  });

  test('applyHpChange undo restores prior participants', () => {
    let e = enc([p('a',10,{maxHp:100,currentHp:100}),p('b',20)]);
    e = { ...e, ...startEncounter(e).patch };
    const before = snap(e);
    const r = applyHpChange(e, 'a', 'damage', 20);
    expect(r.log.undo).toBeTruthy();
    const after = { ...e, ...r.patch, ...r.log.undo };
    expect(snap(after)).toEqual(before);
  });

  test('toggleCondition undo restores prior participants', () => {
    let e = enc([p('a',10),p('b',20)]);
    e = { ...e, ...startEncounter(e).patch };
    const before = snap(e);
    const r = toggleCondition(e, 'a', 'stunned');
    expect(r.log.undo).toBeTruthy();
    const after = { ...e, ...r.patch, ...r.log.undo };
    expect(snap(after)).toEqual(before);
  });

  test('toggleParticipantActive undo restores prior participants + turn order', () => {
    let e = enc([p('a',10),p('b',20),p('c',5)]);
    e = { ...e, ...startEncounter(e).patch };
    const before = snap(e);
    const r = toggleParticipantActive(e, 'b');
    expect(r.log.undo).toBeTruthy();
    const after = { ...e, ...r.patch, ...r.log.undo };
    expect(snap(after)).toEqual(before);
  });

  test('addParticipant undo restores prior participants', () => {
    let e = enc([p('a',10),p('b',20)]);
    e = { ...e, ...startEncounter(e).patch };
    const before = snap(e);
    const np = makeParticipant({ id:'z', name:'z', type:'monster', initiative:15, maxHp:50, currentHp:50 });
    const r = addParticipant(e, np);
    expect(r.log.undo).toBeTruthy();
    const after = { ...e, ...r.patch, ...r.log.undo };
    expect(snap(after)).toEqual(before);
  });

  test('removeParticipant undo restores prior participants + turn order', () => {
    let e = enc([p('a',10),p('b',20),p('c',5)]);
    e = { ...e, ...startEncounter(e).patch };
    const before = snap(e);
    const r = removeParticipant(e, 'b');
    expect(r.log.undo).toBeTruthy();
    const after = { ...e, ...r.patch, ...r.log.undo };
    expect(snap(after)).toEqual(before);
  });

  test('endEncounter undo restores prior state', () => {
    let e = enc([p('a',10),p('b',20)]);
    e = { ...e, ...startEncounter(e).patch };
    const before = snap(e);
    const r = endEncounter(e);
    expect(r.log.undo).toBeTruthy();
    const after = { ...e, ...r.patch, ...r.log.undo };
    expect(snap(after)).toEqual(before);
  });

  test('reorderParticipants has no undo (log: null) — BUG candidate', () => {
    const ps = [p('a',10),p('b',20),p('c',20)]; // b,c tie
    let e = enc(ps);
    e = { ...e, ...startEncounter(e).patch };
    const r = reorderParticipants(e, 'c', 'b');
    // Documents: reorderParticipants returns log: null. Cannot undo.
    // If undo expected here, this is BUG-7.
    expect(r.log).toBeNull();
  });
});
