/**
 * Tests for the startNewLife handler (the death-screen "New Life" replay hook).
 *
 * For T010 this is a generational-free fresh-character reset (sibling to
 * handleRestart) that additionally clears player.lifeSummary so the death
 * screen is dismissed. Heir / inheritance carryover is out of scope (T010b).
 */
import { describe, it, expect, beforeEach } from 'vitest';
import { Person } from '../../src/models/Person.js';
import { Player } from '../../src/models/Player.js';
import { config } from '../../src/config.js';
import { handleStartNewLife } from '../../src/handlers/gameControl.js';
import { handleDeath } from '../../src/services/health/health_manager.js';
import { COMMAND_REGISTRY } from '../../src/handlers/index.js';
import { extractPayload } from '../../src/contracts/extract-payload.js';
import { WEBSOCKET_COMMANDS } from '../../src/contracts/websocket-commands.js';
import { clearAllStatistics } from '../../src/services/retention/statistics.js';

class MockPlayerSession {
  player: Player;
  sentMessages: any[] = [];
  stopped = false;

  constructor(player: Player) {
    this.player = player;
  }

  send(message: any) {
    this.sentMessages.push(message);
  }

  sendPlayerObject() {
    this.sentMessages.push({ type: 'playerObject', player: this.player });
  }

  stop() {
    this.stopped = true;
  }

  async savePlayer() {
    return undefined;
  }
}

function makeDeadPlayer(): Player {
  const character = new Person({
    id: 'char-1',
    firstname: 'Old',
    lastname: 'Timer',
    sex: 'Male',
    status: 'alive',
    ageYears: 90,
    money: 5000,
  } as never);
  const player = new Player({
    userId: 'user-1',
    character,
    status: 'playing',
    controller: 'active',
    r: [new Person({ id: 'r1', firstname: 'Friend', lastname: 'X' } as never)],
  });
  // Kill the player so lifeSummary is populated and status is dead.
  handleDeath(player);
  return player;
}

describe('startNewLife handler', () => {
  beforeEach(() => {
    clearAllStatistics();
  });

  it('resets a dead player back to a playable/creating state', async () => {
    const player = makeDeadPlayer();
    expect(player.c.status).toBe('dead');

    const session = new MockPlayerSession(player);
    await handleStartNewLife(undefined, session as never);

    expect(player.status).toBe('creating');
    expect(player.controller).toBe('inactive');
    expect(player.c.status).toBe('alive');
    expect(player.c.firstname).toBe('');
    expect(player.c.money).toBe(0);
    expect(player.r).toEqual([]);
    expect(player.gameSpeed).toBe(config.SPEED_DEFAULT);
    expect(session.stopped).toBe(true);
    // Sends a fresh player object back to the client.
    expect(session.sentMessages.some((m) => m.type === 'playerObject')).toBe(true);
  });

  it('clears the life summary', async () => {
    const player = makeDeadPlayer();
    expect(player.lifeSummary).toBeTruthy();

    const session = new MockPlayerSession(player);
    await handleStartNewLife(undefined, session as never);

    expect(player.lifeSummary).toBeNull();
  });

  it('is dispatchable and contract/fixtures stay consistent', () => {
    // Registered in the command registry.
    expect(COMMAND_REGISTRY.startNewLife).toBeTypeOf('function');

    // Declared in the contract manifest as client-to-server.
    expect(WEBSOCKET_COMMANDS.startNewLife?.direction).toBe('client-to-server');

    // Type-only envelope resolves to the startNewLife command.
    const { commandType } = extractPayload({ type: 'startNewLife' });
    expect(commandType).toBe('startNewLife');
  });
});
