/**
 * Diamond-sink regression tests (releasable blocker).
 *
 * Before this fix the three diamond sinks were no-ops: the monetization handlers
 * got a bare playerId, so deductDiamonds failed ("Player context missing"), real
 * energy/time never changed, and time-skip always failed for want of a client
 * playerState. These tests lock in that the sinks now charge real diamonds and
 * apply real state, threaded through the authoritative `player` object.
 */
import { describe, it, expect, beforeEach } from 'vitest';
import { PlayerFactory } from '../../src/testing/PlayerFactory.js';
import { purchaseEnergyRefill, clearAllEnergyData } from '../../src/monetization/energyRefills.js';
import { purchaseTimeSkip, advancePlayerClock } from '../../src/monetization/timeSkips.js';

function makePlayer(opts: { diamonds?: number; energy?: number } = {}): any {
  const p: any = PlayerFactory.createAtAge(30);
  p.userId = p.userId || 'sink-test-player';
  p.c.diamonds = opts.diamonds ?? 100;
  p.c.energy = opts.energy ?? 20;
  p.c.peakEnergy = 0;
  return p;
}

describe('Energy refill sink', () => {
  beforeEach(() => clearAllEnergyData());

  it('charges diamonds and raises REAL energy', () => {
    const p = makePlayer({ diamonds: 100, energy: 20 });
    const res = purchaseEnergyRefill(p, 'medium'); // +50 energy, 20 diamonds
    expect(res.success).toBe(true);
    expect(p.c.energy).toBe(70);           // 20 + 50 applied to real state
    expect(p.c.calcEnergy).toBe(70);       // recomputed (peakEnergy 0)
    expect(p.c.diamonds).toBe(80);         // 100 - 20 deducted for real
  });

  it('caps energy at max', () => {
    const p = makePlayer({ diamonds: 100, energy: 90 });
    purchaseEnergyRefill(p, 'full'); // +100, capped at 100
    expect(p.c.energy).toBe(100);
  });

  it('fails (no charge, no energy) when diamonds are insufficient', () => {
    const p = makePlayer({ diamonds: 5, energy: 20 });
    const res = purchaseEnergyRefill(p, 'medium'); // costs 20
    expect(res.success).toBe(false);
    expect(p.c.energy).toBe(20);   // unchanged
    expect(p.c.diamonds).toBe(5);  // unchanged
  });
});

describe('Time skip sink', () => {
  it('charges diamonds, advances the clock, and applies the outcome', () => {
    const p = makePlayer({ diamonds: 100 });
    p.c.occupation = 'work';
    p.c.salary = 4000;
    p.c.status = 'alive';
    const startDays = p.c.ageDays ?? 0;
    const startMoney = p.c.money ?? 0;

    const res = purchaseTimeSkip(p, '1day'); // 24h, 25 diamonds
    expect(res.success).toBe(true);
    expect(p.c.diamonds).toBe(75);                       // 100 - 25 deducted
    expect((p.c.ageDays ?? 0)).toBe(startDays + 1);      // clock advanced 24h
    expect((p.c.money ?? 0)).toBeGreaterThanOrEqual(startMoney); // earnings applied
  });

  it('refuses after death', () => {
    const p = makePlayer({ diamonds: 100 });
    p.c.status = 'dead';
    const res = purchaseTimeSkip(p, '1day');
    expect(res.success).toBe(false);
    expect(p.c.diamonds).toBe(100); // not charged
  });
});

describe('advancePlayerClock', () => {
  it('rolls 24 hours into one day and keeps fields consistent', () => {
    const p = makePlayer();
    p.hourOfDay = 0;
    p.c.ageDays = 100;
    p.c.ageHours = 100 * 24;
    advancePlayerClock(p, 24);
    expect(p.c.ageDays).toBe(101);
    expect(p.hourOfDay).toBe(0);
  });

  it('rolls 365 days into one year', () => {
    const p = makePlayer();
    p.c.ageDays = 365 * 5 - 1; // one day before a birthday
    p.c.ageHours = p.c.ageDays * 24;
    const startYears = p.c.ageYears ?? 0;
    advancePlayerClock(p, 24);
    expect(p.c.ageYears).toBe(startYears + 1);
  });
});
