import { describe, expect, it } from 'vitest';
import {
  CORRECT_LINE,
  MAGGOT_GRACE_MIN,
  judgeEscalation,
  maggotOverdue,
} from '../../src/scenes/floor/escalation';
import type { DrunkStage } from '../../src/data/types';
import {
  CUTOFF_REPLY_CLEAN,
  CUTOFF_REPLY_SCENE,
  CUTOFF_REPLY_SOFT,
  ESCALATION_LINES,
  type EscalationLine,
} from '../../src/data/strings/floor';

const STAGES: readonly DrunkStage[] = ['sober', 'tipsy', 'loose', 'messy', 'maggot'];
const LINES: readonly EscalationLine[] = ['water', 'done', 'escort'];

describe('judgeEscalation matrix', () => {
  it('returns a coherent result for every (stage, line) pair', () => {
    for (const stage of STAGES) {
      for (const line of LINES) {
        const r = judgeEscalation(stage, line);
        expect(['clean', 'scene', 'staggerFree']).toContain(r.outcome);
        expect(r.line).toBe(ESCALATION_LINES[line]);
        expect(r.line.length).toBeGreaterThan(0);
        expect(r.reply.length).toBeGreaterThan(0);
        expect(Object.values(CUTOFF_REPLY_CLEAN)
          .concat(Object.values(CUTOFF_REPLY_SCENE), Object.values(CUTOFF_REPLY_SOFT)))
          .toContain(r.reply);
        expect(r.aggroDelta).toBeGreaterThanOrEqual(0);
        if (r.eject) expect(r.outcome).toBe('clean');
        if (!r.resolved) expect(r.outcome).toBe('staggerFree');
      }
    }
  });

  it('pulls the reply from the bank matching its outcome', () => {
    for (const stage of STAGES) {
      for (const line of LINES) {
        const r = judgeEscalation(stage, line);
        const bank =
          r.outcome === 'clean' ? CUTOFF_REPLY_CLEAN
          : r.outcome === 'scene' ? CUTOFF_REPLY_SCENE
          : CUTOFF_REPLY_SOFT;
        expect(r.reply).toBe(bank[stage]);
      }
    }
  });

  it('is clean whenever the line matches the correct rung', () => {
    for (const stage of STAGES) {
      const r = judgeEscalation(stage, CORRECT_LINE[stage]);
      expect(r.outcome).toBe('clean');
      expect(r.vibeDelta).toBeGreaterThan(0);
      expect(r.aggroDelta).toBe(0);
      expect(r.resolved).toBe(true);
    }
  });

  it('keeps the clean reward modest — never a jackpot for being right', () => {
    for (const stage of STAGES) {
      expect(judgeEscalation(stage, CORRECT_LINE[stage]).vibeDelta).toBeLessThanOrEqual(5);
    }
  });
});

describe('over-escalating', () => {
  it('makes a scene with positive aggro on a loose patron', () => {
    const r = judgeEscalation('loose', 'done');
    expect(r.outcome).toBe('scene');
    expect(r.aggroDelta).toBeGreaterThan(0);
    expect(r.vibeDelta).toBeLessThan(0);
    expect(r.eject).toBe(false);
    expect(r.resolved).toBe(true);
  });

  it('scales aggro and vibe loss with the distance overreached', () => {
    const oneRung = judgeEscalation('loose', 'done');
    const twoRungs = judgeEscalation('loose', 'escort');
    expect(twoRungs.aggroDelta).toBe(oneRung.aggroDelta * 2);
    expect(twoRungs.vibeDelta).toBe(oneRung.vibeDelta * 2);
  });

  it('costs more than restraint does, so aggression never pays', () => {
    const scene = judgeEscalation('sober', 'escort');
    const soft = judgeEscalation('maggot', 'water');
    expect(scene.vibeDelta).toBeLessThan(soft.vibeDelta);
  });
});

describe('under-escalating', () => {
  it('lets a maggot stagger free and leaves the infraction open', () => {
    const r = judgeEscalation('maggot', 'water');
    expect(r.outcome).toBe('staggerFree');
    expect(r.resolved).toBe(false);
    expect(r.eject).toBe(false);
    expect(r.aggroDelta).toBe(0);
    expect(r.vibeDelta).toBeLessThan(0);
  });

  it('does not eject or resolve when you water a messy patron', () => {
    const r = judgeEscalation('messy', 'water');
    expect(r.outcome).toBe('staggerFree');
    expect(r.eject).toBe(false);
    expect(r.resolved).toBe(false);
  });
});

describe('ejection', () => {
  it('escorts a maggot out', () => {
    const r = judgeEscalation('maggot', 'escort');
    expect(r.outcome).toBe('clean');
    expect(r.eject).toBe(true);
  });

  it('walks a messy patron when you call them done', () => {
    expect(judgeEscalation('messy', 'done').eject).toBe(true);
  });

  it('never ejects someone who only needed water', () => {
    for (const stage of ['sober', 'tipsy', 'loose'] as const) {
      expect(judgeEscalation(stage, 'water').eject).toBe(false);
    }
  });
});

describe('maggotOverdue', () => {
  it('is false at the grace boundary and true one minute past', () => {
    expect(maggotOverdue(100, 100 + MAGGOT_GRACE_MIN)).toBe(false);
    expect(maggotOverdue(100, 100 + MAGGOT_GRACE_MIN + 1)).toBe(true);
  });

  it('is false before the grace window elapses', () => {
    expect(maggotOverdue(100, 100)).toBe(false);
    expect(maggotOverdue(100, 110)).toBe(false);
  });
});
