import { describe, expect, it } from 'vitest';
import { BOREDOM_GRACE_SECONDS, BOREDOM_HORIZON_SECONDS, freshness } from './boredom';

describe('boredom decays the display, slowly', () => {
  it('shows everything you earned while production is running', () => {
    expect(freshness(0)).toBe(1);
    expect(freshness(5)).toBe(1);
  });

  it('does not punish a normal jam', () => {
    // The whole grace window plus a beat: a stall long enough to notice, diagnose and fix
    // must leave THE SCREEN exactly as the player left it.
    expect(freshness(BOREDOM_GRACE_SECONDS)).toBe(1);
    expect(freshness(BOREDOM_GRACE_SECONDS - 1)).toBe(1);
  });

  it('takes minutes, not seconds, to forget', () => {
    // A minute of silence has barely dented it; the sky is patient.
    expect(freshness(BOREDOM_GRACE_SECONDS + 30)).toBeGreaterThan(0.8);
    // Fully clean only after the player has genuinely walked away.
    expect(BOREDOM_GRACE_SECONDS + BOREDOM_HORIZON_SECONDS).toBeGreaterThanOrEqual(180);
  });

  it('reaches clean at the horizon and stays there', () => {
    expect(freshness(BOREDOM_GRACE_SECONDS + BOREDOM_HORIZON_SECONDS)).toBe(0);
    expect(freshness(BOREDOM_GRACE_SECONDS + BOREDOM_HORIZON_SECONDS + 9999)).toBe(0);
  });

  it('decays monotonically', () => {
    let prev = 2;
    for (let s = 0; s <= BOREDOM_GRACE_SECONDS + BOREDOM_HORIZON_SECONDS + 60; s += 5) {
      const f = freshness(s);
      expect(f).toBeLessThanOrEqual(prev);
      expect(f).toBeGreaterThanOrEqual(0);
      prev = f;
    }
  });

  it('sits at half way through the decay', () => {
    expect(freshness(BOREDOM_GRACE_SECONDS + BOREDOM_HORIZON_SECONDS / 2)).toBeCloseTo(0.5, 5);
  });

  it('treats a nonsense idle time as fresh rather than wiping the sky', () => {
    expect(freshness(NaN)).toBe(1);
    expect(freshness(-10)).toBe(1);
  });
});

describe('jammed-rot: stuck work spoils faster than resting work', () => {
  const IDLE = BOREDOM_GRACE_SECONDS + 60;

  it('leaves a clean idle exactly as gentle as before', () => {
    // rot defaults to 0, so round 4 behaviour is untouched for a player who walked away.
    expect(freshness(IDLE, 0)).toBe(freshness(IDLE));
  });

  it('forgets faster the more of the factory is jammed', () => {
    const clean = freshness(IDLE, 0);
    const half = freshness(IDLE, 0.5);
    const solid = freshness(IDLE, 1);
    expect(half).toBeLessThan(clean);
    expect(solid).toBeLessThan(half);
  });

  it('still gives a fully jammed factory a grace window', () => {
    // Even total gridlock should not punish instantly — a jam you fix in a few seconds is free.
    expect(freshness(10, 1)).toBe(1);
  });

  it('makes a wholly-stuck line sag in tens of seconds, not minutes', () => {
    expect(freshness(90, 1)).toBeLessThan(0.5);
    expect(freshness(90, 0)).toBeGreaterThan(0.8); // ...while a clean idle has barely moved
  });

  it('clamps nonsense rot values instead of inverting the curve', () => {
    expect(freshness(IDLE, -5)).toBe(freshness(IDLE, 0));
    expect(freshness(IDLE, 99)).toBe(freshness(IDLE, 1));
  });
});
