/**
 * T007b: economyConstants is the single source of truth shared by both game
 * loop paths. These tests pin the scaling-rent formula and the survival rates,
 * and assert that the SAME shared survival function is applied identically.
 */
import { describe, it, expect } from 'vitest';
import { Person } from '../../src/models/Person.js';
import {
  computeWeeklyRent,
  RENT_FLOOR,
  RENT_CAP,
  RENT_INCOME_RATE,
  HUNGER_RATE_PER_HOUR,
  THIRST_RATE_PER_HOUR,
  ENERGY_RESTORE_PER_NIGHT,
  applyHourlySurvival,
} from '../../src/game/economyConstants.js';

describe('computeWeeklyRent (scaling rent sink)', () => {
  it('scales at 30% of weekly income between the floor and cap', () => {
    // weekly income 500 -> 150 (between floor and cap)
    expect(computeWeeklyRent(500)).toBe(500 * RENT_INCOME_RATE);
    expect(computeWeeklyRent(500)).toBe(150);
  });

  it('never falls below the rent floor (low / zero income)', () => {
    expect(computeWeeklyRent(0)).toBe(RENT_FLOOR);
    expect(computeWeeklyRent(100)).toBe(RENT_FLOOR); // 30 < floor
    expect(computeWeeklyRent(-50)).toBe(RENT_FLOOR);
  });

  it('never exceeds the rent cap (very high earners)', () => {
    // weekly income 100k -> 30k raw, capped
    expect(computeWeeklyRent(100_000)).toBe(RENT_CAP);
    expect(computeWeeklyRent(RENT_CAP / RENT_INCOME_RATE + 1000)).toBe(RENT_CAP);
  });

  it('rent rises monotonically with income across the scaling band', () => {
    expect(computeWeeklyRent(300)).toBeLessThan(computeWeeklyRent(600));
    expect(computeWeeklyRent(600)).toBeLessThan(computeWeeklyRent(1200));
  });
});

describe('survival rate constants', () => {
  it('hunger/thirst rise at the audited rates', () => {
    expect(HUNGER_RATE_PER_HOUR).toBe(3);
    expect(THIRST_RATE_PER_HOUR).toBe(4);
  });

  it('overnight energy restore is the unified value', () => {
    // Eased from 25 -> 35 to relieve energy scarcity that was locking players out
    // of activities in live play.
    expect(ENERGY_RESTORE_PER_NIGHT).toBe(35);
  });
});

describe('applyHourlySurvival (one shared implementation, online == offline)', () => {
  function fresh(): Person {
    return new Person({
      id: 'p',
      firstname: 'A',
      lastname: 'B',
      sex: 'Male',
      hunger: 10,
      thirst: 10,
      health: 100,
      energy: 100,
      calcEnergy: 100,
      happiness: 50,
    } as never);
  }

  it('raises hunger/thirst by the per-hour rates (clamped 0-100)', () => {
    const p = fresh();
    applyHourlySurvival(p);
    expect(p.hunger).toBe(13);
    expect(p.thirst).toBe(14);
  });

  it('regenerates health when fed/hydrated and not exhausted', () => {
    const p = fresh();
    p.health = 90;
    applyHourlySurvival(p);
    expect(p.health).toBe(90.5);
  });

  it('produces identical results when applied to two equal fixtures (parity)', () => {
    const online = fresh();
    const offline = fresh();
    applyHourlySurvival(online);
    applyHourlySurvival(offline);
    expect(online.hunger).toBe(offline.hunger);
    expect(online.thirst).toBe(offline.thirst);
    expect(online.health).toBe(offline.health);
  });
});
