// SLOT-NOT-SORT invariant: addParticipant + updateParticipant must PRESERVE
// manually-dragged tie order (same-initiative participants).
//
// Design doc (docs/INITIATIVE_ORDERING.md):
//   "Re-slotting on add/edit must preserve drag-established tie order."
//   "Tie-break = original add order. Later additions slot AFTER existing
//    same-init participants. Stable insertion."
//
// startEncounter re-sorts once to freeze the list; after that add/update use
// stable slot insertion (slotIndexForInit), never wholesale re-sort — so drag
// tie order survives.
//
// New API: addParticipant / updateParticipant / reorderParticipants are async,
// take ctx, return newEnc.

'use strict';

const {
  makeParticipant,
  startEncounter, addParticipant, updateParticipant, reorderParticipants,
} = require('@ttrpg/shared');
const { mockCtx } = require('./_helpers');

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

describe('SLOT-NOT-SORT: drag tie order survives add/edit', () => {
  test('addParticipant preserves existing drag tie order', async () => {
    const { ctx } = mockCtx();
    // Two same-init participants. DM drags b BEFORE a (tie override).
    let e = enc([p('a', 10), p('b', 10)]);
    e = await reorderParticipants(e, 'b', 'a', ctx); // drag b ahead of a → [b,a]
    expect(e.participants.map(x => x.id)).toEqual(['b', 'a']);

    // Add unrelated participant (different init). Tie order [b,a] MUST survive.
    e = await addParticipant(e, p('c', 5), ctx);
    const ids = e.participants.map(x => x.id);
    // c slots last (init 5 < 10). b,a tie order preserved.
    expect(ids).toEqual(['b', 'a', 'c']);
  });

  test('addParticipant with same init as dragged pair slots AFTER tie group', async () => {
    const { ctx } = mockCtx();
    // DM drags b before a (both init 10). Then add d also init 10.
    // d must slot AFTER existing same-init pair (stable insertion), not re-sort.
    let e = enc([p('a', 10), p('b', 10)]);
    e = await reorderParticipants(e, 'b', 'a', ctx); // [b,a]
    e = await addParticipant(e, p('d', 10), ctx); // init 10, after tie group
    const ids = e.participants.map(x => x.id);
    expect(ids).toEqual(['b', 'a', 'd']);
  });

  test('updateParticipant preserves drag tie order on unrelated field edit', async () => {
    const { ctx } = mockCtx();
    let e = enc([p('a', 10), p('b', 10)]);
    e = await reorderParticipants(e, 'b', 'a', ctx); // [b,a]
    // Edit a's name (not initiative). Tie order MUST survive.
    e = await updateParticipant(e, 'a', { name: 'A2' }, ctx);
    expect(e.participants.map(x => x.id)).toEqual(['b', 'a']);
  });

  test('updateParticipant init change slots, preserves OTHER tie group order', async () => {
    const { ctx } = mockCtx();
    // Two tie groups: [a,b] init 10, [c,d] init 5. DM drags b before a.
    let e = enc([p('a', 10), p('b', 10), p('c', 5), p('d', 5)]);
    e = await reorderParticipants(e, 'b', 'a', ctx); // [b,a,c,d]
    // Edit c's initiative to 10 — slots into top group. [c] order within its
    // own old group irrelevant (it leaves that group). But [b,a] tie MUST stay.
    e = await updateParticipant(e, 'c', { initiative: 10 }, ctx);
    const topTwo = e.participants.filter(x => x.initiative === 10).map(x => x.id);
    // b before a preserved. c joins the 10-group (exact slot less critical
    // than b,a order surviving).
    expect(topTwo.indexOf('b')).toBeLessThan(topTwo.indexOf('a'));
  });
});
