import { BossController } from '@logic/BossController';
import { BANNED_RESCUE_SEQUENCE, ChapterSettlement } from '@logic/ChapterSettlement';
import { IBossConfig } from '@data/Interfaces';

const bossCfg: IBossConfig = {
    id: 'shuang_huan_fang',
    displayName: '双幻坊',
    hp: 3,
    butterflyReveal: true,
    princessCutsceneAtHpRatio: 0.5,
    phases: [
        { hpThreshold: 1.0, mode: 'pair_pincer', actionIntervalSec: 2.2 },
        { hpThreshold: 0.66, mode: 'fireball_spread', actionIntervalSec: 1.8 },
        { hpThreshold: 0.33, mode: 'clone_confuse', actionIntervalSec: 1.4 },
    ],
};

describe('BossController — butterfly reveal (req 9.1-9.3)', () => {
    it('body hits are ignored until butterfly is hit', () => {
        const boss = new BossController(bossCfg);
        expect(boss.onBodyHit()).toEqual([]);
        expect(boss.currentHp).toBe(3);
    });

    it('butterfly hit emits reveal event once', () => {
        const boss = new BossController(bossCfg);
        expect(boss.onButterflyHit()).toEqual([{ kind: 'butterfly_revealed' }]);
        expect(boss.onButterflyHit()).toEqual([]); // second hit is a no-op
        expect(boss.isButterflyRevealed).toBe(true);
    });

    it('body hits after reveal decrement HP one at a time', () => {
        const boss = new BossController(bossCfg);
        boss.onButterflyHit();
        boss.onBodyHit();
        expect(boss.currentHp).toBe(2);
    });
});

describe('BossController — phase transitions (req 9.4)', () => {
    it('HP drop to 2/3 triggers phase_changed to fireball_spread', () => {
        const boss = new BossController(bossCfg);
        boss.onButterflyHit();
        const events = boss.onBodyHit(); // 3 → 2 (ratio 0.66)
        expect(events.some((e) => e.kind === 'phase_changed' && e.phase === 'fireball_spread')).toBe(true);
    });

    it('HP drop to 1/3 triggers clone_confuse', () => {
        const boss = new BossController(bossCfg);
        boss.onButterflyHit();
        boss.onBodyHit();
        const events = boss.onBodyHit(); // 2 → 1 (ratio 0.33)
        expect(events.some((e) => e.kind === 'phase_changed' && e.phase === 'clone_confuse')).toBe(true);
    });
});

describe('BossController — princess cutscene + death (req 8.6, 14.1)', () => {
    it('emits princess_taken_cutscene when HP reaches 1/2', () => {
        const boss = new BossController(bossCfg);
        boss.onButterflyHit();
        const events = boss.onBodyHit(); // 3 → 2 → ratio 0.66 (> 0.5, no cutscene yet)
        expect(events.some((e) => e.kind === 'princess_taken_cutscene')).toBe(false);
        const events2 = boss.onBodyHit(); // 2 → 1 → ratio 0.33 (< 0.5)
        expect(events2.some((e) => e.kind === 'princess_taken_cutscene')).toBe(true);
    });

    it('emits boss_killed on final hit and marks isDead', () => {
        const boss = new BossController(bossCfg);
        boss.onButterflyHit();
        boss.onBodyHit();
        boss.onBodyHit();
        const events = boss.onBodyHit();
        expect(events.some((e) => e.kind === 'boss_killed')).toBe(true);
        expect(boss.isDead).toBe(true);
    });

    it('further body hits after death are no-ops', () => {
        const boss = new BossController(bossCfg);
        boss.onButterflyHit();
        boss.onBodyHit();
        boss.onBodyHit();
        boss.onBodyHit();
        expect(boss.onBodyHit()).toEqual([]);
    });
});

describe('ChapterSettlement — rope-cut ban (req 14.5)', () => {
    it('assertCutsceneAllowed throws on every banned id', () => {
        const cs = new ChapterSettlement({
            totalScore: 0,
            stageScore: 0,
            comboCount: 0,
            flawless: true,
            remainingTimeSec: 0,
        });
        for (const bad of BANNED_RESCUE_SEQUENCE) {
            expect(() => cs.assertCutsceneAllowed(bad)).toThrow(/banned/);
        }
    });

    it('allows the legitimate cutscene ids', () => {
        const cs = new ChapterSettlement({
            totalScore: 0,
            stageScore: 0,
            comboCount: 0,
            flawless: true,
            remainingTimeSec: 0,
        });
        expect(() => cs.assertCutsceneAllowed('princess_taken')).not.toThrow();
        expect(() => cs.assertCutsceneAllowed('boss_killed_freeze')).not.toThrow();
        expect(() => cs.assertCutsceneAllowed('settlement_screen')).not.toThrow();
    });

    it('build() returns the "princess taken" closing line, not a rescue one', () => {
        const cs = new ChapterSettlement({
            totalScore: 1000,
            stageScore: 500,
            comboCount: 2,
            flawless: true,
            remainingTimeSec: 10,
        });
        const r = cs.build();
        expect(r.closingLine).toMatch(/公主被带走/);
        expect(r.closingLine).not.toMatch(/救/);
    });
});
