/**
 * Death is now a SINGLE shared path: health_manager.handleDeath.
 *
 * Both the online path (PlayerSession.processDayTick) and the offline path
 * (GameEngine death check) route through this one function, so a dead player
 * ends up in identical state regardless of which loop killed them:
 *  - controller === 'inactive'
 *  - c.status === 'dead'
 *  - a queued "You have died!" message (deduped)
 *  - a populated player.lifeSummary
 */
import { describe, it, expect, beforeEach } from 'vitest';
import { Person } from '../../src/models/Person.js';
import { Player } from '../../src/models/Player.js';
import { handleDeath } from '../../src/services/health/health_manager.js';
import { getGameEngine } from '../../src/game/engine/GameEngine.js';
import { clearAllStatistics } from '../../src/services/retention/statistics.js';

function makeLivePlayer(userId: string): Player {
  const character = new Person({
    id: `c-${userId}`,
    firstname: 'Test',
    lastname: 'Subject',
    sex: 'Male',
    status: 'alive',
    ageYears: 75,
    money: 12000,
  } as never);
  return new Player({
    userId,
    character,
    status: 'playing',
    controller: 'active',
    r: [new Person({ id: 'r1', firstname: 'Pal', lastname: 'X' } as never)],
  });
}

describe('shared death path', () => {
  beforeEach(() => {
    clearAllStatistics();
  });

  it('handleDeath sets dead/inactive, queues the message, and builds lifeSummary', () => {
    const player = makeLivePlayer('p1');

    handleDeath(player);

    expect(player.c.status).toBe('dead');
    expect(player.controller).toBe('inactive');
    expect(player.messageQueue).toContain('You have died!');
    expect(player.lifeSummary).toBeTruthy();
    expect(player.lifeSummary?.finalAge).toBe(75);
    expect(player.lifeSummary?.score).toBeGreaterThan(0);
  });

  it('is idempotent: a second call does not duplicate the message or rebuild the summary', () => {
    const player = makeLivePlayer('p2');

    handleDeath(player);
    const firstSummary = player.lifeSummary;
    handleDeath(player);

    const deathMessages = player.messageQueue.filter((m) => m === 'You have died!');
    expect(deathMessages.length).toBe(1);
    // Same object reference -> not rebuilt.
    expect(player.lifeSummary).toBe(firstSummary);
  });

  it('online and offline death produce identical observable state', () => {
    // "Online" player: directly killed via the shared handleDeath (the path
    // PlayerSession.processDayTick now uses).
    const online = makeLivePlayer('online');
    handleDeath(online);

    // "Offline" player: killed via GameEngine.handleDeath, which delegates to
    // the same health_manager.handleDeath.
    const offline = makeLivePlayer('offline');
    const engine = getGameEngine();
    engine.handleDeath(offline);

    expect(offline.c.status).toBe(online.c.status);
    expect(offline.controller).toBe(online.controller);
    expect(offline.messageQueue).toEqual(online.messageQueue);

    // Both populate a lifeSummary with the same structural shape.
    expect(online.lifeSummary).toBeTruthy();
    expect(offline.lifeSummary).toBeTruthy();
    expect(Object.keys(offline.lifeSummary!).sort()).toEqual(
      Object.keys(online.lifeSummary!).sort()
    );
    expect(offline.lifeSummary?.finalAge).toBe(online.lifeSummary?.finalAge);
  });
});
