/**
 * T007b: energy scarcity. When usable energy (calcEnergy) is exhausted (0), the
 * character is forced to rest — energy-costing activities are blocked — and a
 * small per-hour health/happiness penalty is applied for pushing on with no
 * reserve.
 */
import { describe, it, expect } from 'vitest';
import { Person } from '../../src/models/Person.js';
import {
  isTooExhaustedForActivity,
  getPlayerActivity,
} from '../../src/game/engine/intradayActivity.js';
import {
  applyHourlySurvival,
  EXHAUSTION_HEALTH_PENALTY_PER_HOUR,
  EXHAUSTION_HAPPINESS_PENALTY_PER_HOUR,
} from '../../src/game/economyConstants.js';

function person(over: Partial<Record<string, number>> = {}): Person {
  return new Person({
    id: 'p',
    firstname: 'A',
    lastname: 'B',
    sex: 'Male',
    hunger: 10,
    thirst: 10,
    health: 100,
    energy: 0,
    calcEnergy: 0,
    happiness: 50,
    ...over,
  } as never);
}

describe('energy gate blocks energy-costing activities (RAW energy)', () => {
  // The activity gate matches the server handler + iOS client: it gates on RAW
  // `energy`, NOT calcEnergy. calcEnergy = energy - peakEnergy, and a high-school
  // student carries peakEnergy ~20 permanently, so a calcEnergy gate would falsely
  // block every activity even with plenty of raw energy.
  it('blocks an energy-costing activity when raw energy cannot pay the cost', () => {
    const p = person({ energy: 0, calcEnergy: 0 });
    const study = getPlayerActivity('study')!; // energyCost 10
    expect(study.energyCost).toBeGreaterThan(0);
    expect(isTooExhaustedForActivity(p, study)).toBe(true);
  });

  it('allows an energy-costing activity when raw energy is available', () => {
    const p = person({ energy: 40, calcEnergy: 40 });
    const exercise = getPlayerActivity('exercise')!; // energyCost 15
    expect(isTooExhaustedForActivity(p, exercise)).toBe(false);
  });

  it('does NOT block a normal-energy student whose calcEnergy is near 0 (peakEnergy reserve)', () => {
    // Regression: an age-18 student with raw energy 40 but peakEnergy 20 has
    // calcEnergy ~20 (and can dip to ~0). The old calcEnergy<=0 gate locked out
    // every activity. Raw-energy gating correctly allows a cost-10 activity.
    const p = person({ energy: 40, peakEnergy: 20, calcEnergy: 0 });
    expect(isTooExhaustedForActivity(p, { energyCost: 10 })).toBe(false);
  });

  it('blocks a 0-energy player from a cost-10 activity', () => {
    const p = person({ energy: 0, peakEnergy: 20, calcEnergy: 0 });
    expect(isTooExhaustedForActivity(p, { energyCost: 10 })).toBe(true);
  });

  it('never blocks a zero-cost activity', () => {
    const p = person({ energy: 0, calcEnergy: 0 });
    expect(isTooExhaustedForActivity(p, { energyCost: 0 })).toBe(false);
  });
});

describe('energy scarcity applies a per-hour penalty', () => {
  it('exhausted character loses health and happiness each hour', () => {
    const p = person({ calcEnergy: 0, health: 90, happiness: 50, hunger: 10, thirst: 10 });
    applyHourlySurvival(p);
    // Fed/hydrated so the base path would have regen'd +0.5; the exhaustion
    // penalty (-1 health) nets the health below 90, and happiness drops.
    expect(p.health).toBe(90 + 0.5 - EXHAUSTION_HEALTH_PENALTY_PER_HOUR);
    expect(p.happiness).toBe(50 - EXHAUSTION_HAPPINESS_PENALTY_PER_HOUR);
  });

  it('rested character takes no exhaustion penalty', () => {
    const p = person({ calcEnergy: 50, health: 90, happiness: 50, hunger: 10, thirst: 10 });
    applyHourlySurvival(p);
    expect(p.happiness).toBe(50); // no penalty
    expect(p.health).toBe(90.5); // just the normal regen
  });
});
