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

import { DEFAULT_NPC_TOKEN_SESSION_SCALE } from '../../shared/types/appPlayers';

import { SceneNpcTokensSessionStore } from './sceneNpcTokensSessionStore';

void test('SceneNpcTokensSessionStore move and reset', () => {
  const store = new SceneNpcTokensSessionStore();
  const s1 = store.dispatch({ kind: 'move', placementId: 'a', nx: 0.2, ny: 0.3 });
  assert.equal(s1.byPlacementId.a?.nx, 0.2);
  assert.equal(s1.revision, 2);
  const s2 = store.dispatch({ kind: 'move', placementId: 'a', nx: 0.2, ny: 0.3 });
  assert.equal(s2.revision, s1.revision); // no-op
  const s3 = store.reset();
  assert.deepEqual(s3.byPlacementId, {});
  assert.equal(s3.scale, DEFAULT_NPC_TOKEN_SESSION_SCALE);
  assert.ok(s3.revision > s1.revision);
});

void test('SceneNpcTokensSessionStore setScale', () => {
  const store = new SceneNpcTokensSessionStore();
  const s1 = store.dispatch({ kind: 'setScale', scale: 1.5 });
  assert.equal(s1.scale, 1.5);
  store.dispatch({ kind: 'move', placementId: 'a', nx: 0.1, ny: 0.2 });
  const s2 = store.dispatch({ kind: 'setScale', scale: 0.1 });
  assert.equal(s2.scale, 0.4); // clamped min
  assert.ok(s2.byPlacementId.a);
  const s3 = store.reset();
  assert.equal(s3.scale, DEFAULT_NPC_TOKEN_SESSION_SCALE);
  assert.deepEqual(s3.byPlacementId, {});
});

void test('SceneNpcTokensSessionStore setDisposition without inventing position', () => {
  const store = new SceneNpcTokensSessionStore();
  const s1 = store.dispatch({
    kind: 'setDisposition',
    placementId: 'a',
    disposition: 'hostile',
  });
  assert.equal(s1.byPlacementId.a?.disposition, 'hostile');
  assert.equal(s1.byPlacementId.a?.nx, undefined);
  assert.equal(s1.byPlacementId.a?.ny, undefined);
  const s2 = store.dispatch({ kind: 'move', placementId: 'a', nx: 0.4, ny: 0.6 });
  assert.equal(s2.byPlacementId.a?.nx, 0.4);
  assert.equal(s2.byPlacementId.a?.disposition, 'hostile');
});

void test('SceneNpcTokensSessionStore spawn is session-only and despawn removes it', () => {
  const store = new SceneNpcTokensSessionStore();
  const s1 = store.dispatch({
    kind: 'spawn',
    npcId: 'npc1',
    sceneId: 'sceneA',
    nx: 0.4,
    ny: 0.6,
    disposition: 'hostile',
  });
  const ids = Object.keys(s1.spawned);
  assert.equal(ids.length, 1);
  const spawn = s1.spawned[ids[0]!];
  assert.equal(spawn?.npcId, 'npc1');
  assert.equal(spawn?.sceneId, 'sceneA');
  assert.equal(spawn?.nx, 0.4);
  const s2 = store.dispatch({ kind: 'move', placementId: ids[0]!, nx: 0.7, ny: 0.2 });
  assert.equal(s2.spawned[ids[0]!]?.nx, 0.7);
  const s3 = store.dispatch({ kind: 'despawn', placementId: ids[0]! });
  assert.deepEqual(s3.spawned, {});
  assert.equal(s3.byPlacementId[ids[0]!], undefined);
});

void test('SceneNpcTokensSessionStore setInactive is session flag', () => {
  const store = new SceneNpcTokensSessionStore();
  const s1 = store.dispatch({ kind: 'setInactive', placementId: 'a', inactive: true });
  assert.equal(s1.byPlacementId.a?.inactive, true);
  const s2 = store.dispatch({ kind: 'setInactive', placementId: 'a', inactive: false });
  assert.deepEqual(s2.byPlacementId, {});
  store.dispatch({ kind: 'move', placementId: 'a', nx: 0.2, ny: 0.3 });
  store.dispatch({ kind: 'setInactive', placementId: 'a', inactive: true });
  const s3 = store.dispatch({ kind: 'setInactive', placementId: 'a', inactive: false });
  assert.equal(s3.byPlacementId.a?.nx, 0.2);
  assert.equal(s3.byPlacementId.a?.inactive, undefined);
});

void test('SceneNpcTokensSessionStore setSize is session-only and survives move', () => {
  const store = new SceneNpcTokensSessionStore();
  const s1 = store.dispatch({ kind: 'setSize', placementId: 'a', sizeN: 0.2 });
  assert.equal(s1.byPlacementId.a?.sizeN, 0.2);
  assert.equal(s1.byPlacementId.a?.nx, undefined);
  const s2 = store.dispatch({ kind: 'move', placementId: 'a', nx: 0.3, ny: 0.4 });
  assert.equal(s2.byPlacementId.a?.sizeN, 0.2);
  assert.equal(s2.byPlacementId.a?.nx, 0.3);
  const spawned = store.dispatch({
    kind: 'spawn',
    npcId: 'npc1',
    sceneId: 'sceneA',
    nx: 0.5,
    ny: 0.5,
  });
  const id = Object.keys(spawned.spawned)[0]!;
  const s3 = store.dispatch({ kind: 'setSize', placementId: id, sizeN: 0.18 });
  assert.equal(s3.spawned[id]?.sizeN, 0.18);
  assert.equal(s3.byPlacementId[id]?.sizeN, 0.18);
});
