import { test } from 'node:test';
import assert from 'node:assert/strict';
import { createInitialState, reduce, getLegalIntents, isPlayersTurn } from './game.js';

function join(state, playerId, name, seat) {
  return reduce(state, { type: 'JOIN', playerId, name, seat, color: '#000' });
}

test('lobby: join validation', () => {
  let state = createInitialState();
  state = join(state, 'p1', 'Alice', 1);
  assert.throws(() => join(state, 'p1', 'Alice again', 2), /already joined/);
  assert.throws(() => join(state, 'p2', 'Bob', 1), /Seat already taken/);
  assert.throws(() => reduce(state, { type: 'START_GAME' }), /Not enough players/);
});

test('full game: both choice points, race to finish', () => {
  let state = createInitialState();
  state = join(state, 'p1', 'Alice', 1);
  state = join(state, 'p2', 'Bob', 2);
  state = reduce(state, { type: 'START_GAME' });

  assert.equal(state.status, 'active');
  assert.deepEqual(state.turnOrder, ['p1', 'p2']);
  assert.equal(state.currentTurn, 'p1');
  assert.deepEqual(getLegalIntents(state, 'p1'), ['REQUEST_ROLL']);
  assert.deepEqual(getLegalIntents(state, 'p2'), []);
  assert.equal(isPlayersTurn(state, 'p2'), false);

  // Not p2's turn yet.
  assert.throws(() => reduce(state, { type: 'ROLL', playerId: 'p2', value: 3 }), /Not your turn/);

  // p1 rolls onto the first choice space; movement stops immediately even
  // though only 1 of the roll's pips was needed.
  state = reduce(state, { type: 'ROLL', playerId: 'p1', value: 3 });
  assert.equal(state.players.p1.position, 'crossroads');
  assert.deepEqual(state.players.p1.pendingChoice, { atSpace: 'crossroads', options: ['career_1', 'edu_1'] });
  assert.equal(state.currentTurn, 'p1', 'turn stays with the player until they choose');
  assert.deepEqual(getLegalIntents(state, 'p1'), ['REQUEST_CHOOSE']);

  assert.throws(() => reduce(state, { type: 'ROLL', playerId: 'p1', value: 2 }), /Resolve pending choice/);
  assert.throws(() => reduce(state, { type: 'CHOOSE', playerId: 'p1', spaceId: 'finish' }), /Illegal choice/);

  state = reduce(state, { type: 'CHOOSE', playerId: 'p1', spaceId: 'career_1' });
  assert.equal(state.players.p1.position, 'career_1');
  assert.equal(state.players.p1.pendingChoice, null);
  assert.equal(state.currentTurn, 'p2', 'choosing resolves the turn');

  // p2 takes the education branch.
  state = reduce(state, { type: 'ROLL', playerId: 'p2', value: 3 });
  assert.equal(state.players.p2.position, 'crossroads');
  state = reduce(state, { type: 'CHOOSE', playerId: 'p2', spaceId: 'edu_1' });
  assert.equal(state.players.p2.position, 'edu_1');
  assert.equal(state.currentTurn, 'p1');

  // p1: career_1 -> life_crossroads is exactly 6 steps; only the landed
  // space's cash effect applies, not spaces merely passed through.
  state = reduce(state, { type: 'ROLL', playerId: 'p1', value: 6 });
  assert.equal(state.players.p1.position, 'life_crossroads');
  assert.equal(state.players.p1.cash, 0, 'passed-through Paycheck/Drama/Review do not apply');
  state = reduce(state, { type: 'CHOOSE', playerId: 'p1', spaceId: 'investment_1' });
  assert.equal(state.players.p1.position, 'investment_1');
  assert.equal(state.players.p1.cash, -150);
  assert.equal(state.currentTurn, 'p2');

  // p2: edu_1 -> life_crossroads is also exactly 6 steps.
  state = reduce(state, { type: 'ROLL', playerId: 'p2', value: 6 });
  assert.equal(state.players.p2.position, 'life_crossroads');
  state = reduce(state, { type: 'CHOOSE', playerId: 'p2', spaceId: 'relationship_1' });
  assert.equal(state.players.p2.position, 'relationship_1');
  assert.equal(state.currentTurn, 'p1');

  // p1: investment_1 -> high_risk_choice is exactly 4 steps.
  state = reduce(state, { type: 'ROLL', playerId: 'p1', value: 4 });
  assert.equal(state.players.p1.position, 'high_risk_choice');
  state = reduce(state, { type: 'CHOOSE', playerId: 'p1', spaceId: 'safe_1' });
  assert.equal(state.players.p1.cash, -50); // -150 + 100
  assert.equal(state.currentTurn, 'p2');

  // p2: relationship_1 -> high_risk_choice is also exactly 4 steps.
  state = reduce(state, { type: 'ROLL', playerId: 'p2', value: 4 });
  assert.equal(state.players.p2.position, 'high_risk_choice');
  state = reduce(state, { type: 'CHOOSE', playerId: 'p2', spaceId: 'high_risk_1' });
  assert.equal(state.players.p2.cash, 500);
  assert.equal(state.currentTurn, 'p1');

  // p1: safe_1 -> finish is exactly 3 steps. First arrival ends the game.
  assert.equal(state.status, 'active');
  state = reduce(state, { type: 'ROLL', playerId: 'p1', value: 3 });
  assert.equal(state.players.p1.position, 'finish');
  assert.equal(state.status, 'finished');
  assert.equal(state.winnerId, 'p1');
  assert.equal(state.currentTurn, null);
  assert.deepEqual(getLegalIntents(state, 'p2'), []);

  assert.throws(() => reduce(state, { type: 'ROLL', playerId: 'p2', value: 1 }), /not active/);
});
