'use strict';

const shared = require('@ttrpg/shared');
const { makeParticipant, startEncounter, nextTurn, applyHpChange, deathSave } = shared;
const { mockCtx } = require('./_helpers');

function pc(id, init, extra = {}) {
  return makeParticipant({
    id,
    name: id,
    type: 'character',
    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 byId = (e, id) => e.participants.find(p => p.id === id);

describe('death state: participant remains in initiative and encounter', () => {
  test('dropping to 0 keeps PC active, in participants, and in turnOrderIds', async () => {
    const { ctx } = mockCtx();
    let e = enc([pc('a', 20), pc('b', 15), pc('c', 10)]);
    e = await startEncounter(e, ctx);
    const orderBefore = e.turnOrderIds.slice();

    e = await applyHpChange(e, 'b', 'damage', 100, ctx);

    expect(byId(e, 'b').status).toBe('dying');
    expect(byId(e, 'b').isActive).not.toBe(false);
    expect(e.participants.map(p => p.id)).toContain('b');
    expect(e.turnOrderIds).toEqual(orderBefore);
  });

  test('dead PC remains in participants and turnOrderIds after three failures', async () => {
    const { ctx } = mockCtx();
    let e = enc([pc('a', 20), pc('b', 15), pc('c', 10)]);
    e = await startEncounter(e, ctx);
    const orderBefore = e.turnOrderIds.slice();
    e = await applyHpChange(e, 'b', 'damage', 100, ctx);

    e = (await deathSave(e, 'b', 'fail', ctx)).enc;
    e = (await deathSave(e, 'b', 'fail', ctx)).enc;
    e = (await deathSave(e, 'b', 'fail', ctx)).enc;

    expect(byId(e, 'b').status).toBe('dead');
    expect(byId(e, 'b').isActive).not.toBe(false);
    expect(e.participants.map(p => p.id)).toContain('b');
    expect(e.turnOrderIds).toEqual(orderBefore);
  });

  test('nextTurn may still land on dead PC because DM/tool events may matter', async () => {
    const { ctx } = mockCtx();
    let e = enc([pc('a', 20), pc('b', 15), pc('c', 10)]);
    e = await startEncounter(e, ctx);
    e = await applyHpChange(e, 'b', 'damage', 100, ctx);
    e = (await deathSave(e, 'b', 'fail', ctx)).enc;
    e = (await deathSave(e, 'b', 'fail', ctx)).enc;
    e = (await deathSave(e, 'b', 'fail', ctx)).enc;

    e = await nextTurn(e, ctx);

    expect(e.currentTurnParticipantId).toBe('b');
    expect(byId(e, 'b').status).toBe('dead');
  });

  test('deathSave rejected/no-op once dead', async () => {
    const { ctx } = mockCtx();
    let e = enc([pc('a', 20), pc('b', 15)]);
    e = await startEncounter(e, ctx);
    e = await applyHpChange(e, 'b', 'damage', 100, ctx);
    e = (await deathSave(e, 'b', 'fail', ctx)).enc;
    e = (await deathSave(e, 'b', 'fail', ctx)).enc;
    e = (await deathSave(e, 'b', 'fail', ctx)).enc;
    const before = byId(e, 'b');

    try { e = (await deathSave(e, 'b', 'fail', ctx)).enc; } catch (err) {}

    expect(byId(e, 'b')).toMatchObject(before);
  });
});
