import { describe, expect, it } from 'vitest';
import { GameState, Inventory } from '@rpg/engine';
import { QUESTS, QUEST_FLOWERS, activeStage, questDialogueFor, questLog, questTracker } from '../quests';
import { applyDialogueEffect, type DialogueEffectSink } from '../../systems/DialogueEffects';
import { DIALOGUE_CUSTOM } from '../effects';

const bells = QUESTS[0]!;

describe('квест «Три цветка» — стадии', () => {
    it('до знакомства квест не взят, стадий нет', () => {
        const s = new GameState();
        expect(questLog(s)).toEqual([]);
    });

    it('после взятия активна стадия сбора; диалог сдачи не готов', () => {
        const s = new GameState();
        s.setFlag('quest_bells_taken');
        expect(activeStage(s, bells)?.dialogue).toBe('elder_hand_in');
        expect(questDialogueFor(s, 'elder')).toBeNull(); // цветов мало
        expect(questLog(s)).toEqual([
            { done: true, text: 'Пепел дышит у прудов. Поговорить с Ирвином' },
            { done: false, text: `Собрать три колокольчика — Серые пруды, северо-запад лугов (0/${QUEST_FLOWERS})` }
        ]);
    });

    it('собрано достаточно — сдача готова', () => {
        const s = new GameState();
        s.setFlag('quest_bells_taken');
        s.setVar('flowers', QUEST_FLOWERS);
        expect(questDialogueFor(s, 'elder')).toBe('elder_hand_in');
        expect(questLog(s)).toEqual([
            { done: true, text: 'Пепел дышит у прудов. Поговорить с Ирвином' },
            { done: false, text: `Собрать три колокольчика — Серые пруды, северо-запад лугов (3/3)` }
        ]);
    });

    it('после сдачи — эпилог с Милой, до разговора с ней активен', () => {
        const s = new GameState();
        s.setFlag('quest_bells_taken');
        s.setVar('flowers', QUEST_FLOWERS);
        s.setFlag('quest_bells_done');
        expect(questDialogueFor(s, 'trader')).toBe('trader_after');
        expect(questDialogueFor(s, 'elder')).toBeNull(); // Ирвин — обычный repeat
        expect(questLog(s)).toEqual([
            { done: true, text: 'Пепел дышит у прудов. Поговорить с Ирвином' },
            { done: true, text: `Собрать три колокольчика — Серые пруды, северо-запад лугов (3/3)` },
            { done: false, text: 'Поговорить с Милой — поляна у тропы гудит' }
        ]);
        // Разговор с Милой (act2_hook) завершает и эпилог.
        s.setFlag('act2_hook');
        expect(questLog(s).every((e) => e.done)).toBe(true);
    });
});

describe('трекер HUD (questTracker)', () => {
    it('в свежем состоянии — цель взятия (поговорить с Ирвином)', () => {
        const s = new GameState();
        expect(questTracker(s)).toBe('Пепел дышит у прудов. Поговорить с Ирвином');
    });

    it('на стадии сбора показывает прогресс', () => {
        const s = new GameState();
        s.setFlag('quest_bells_taken');
        s.setVar('flowers', 1);
        expect(questTracker(s)).toBe(`Собрать три колокольчика — Серые пруды, северо-запад лугов (1/3)`);
    });

    it('после завершения квеста колокольчиков — цель следующего квеста', () => {
        const s = new GameState();
        s.setFlag('quest_bells_taken');
        s.setFlag('quest_bells_done');
        s.setFlag('act2_hook');
        expect(questTracker(s)).toBe('Резонатор Клинта молчит. Поговорить с Клинтом');
    });
});

describe('эффекты диалогов', () => {
    it('applyDialogueEffect: give/take меняют сумку, unknown игнорируется', () => {
        const inv = new Inventory();
        const sink: DialogueEffectSink = {
            giveItem: (id, count) => void inv.add(id, count),
            takeItem: (id, count) => void inv.remove(id, count),
            playSound: () => {},
            showToast: () => {},
            custom: () => {}
        };
        applyDialogueEffect({ kind: 'giveItem', id: 'cloth', count: 2 }, sink);
        expect(inv.count('cloth')).toBe(2);
        applyDialogueEffect({ kind: 'takeItem', id: 'cloth' }, sink);
        expect(inv.count('cloth')).toBe(1);
        // неизвестный kind — тихий игнор (валидатор ловит как error)
        applyDialogueEffect({ kind: 'custom', id: 'nope' } as never, sink);
        applyDialogueEffect({ kind: 'toast', text: '' }, sink);
        expect(inv.count('cloth')).toBe(1);
    });

    it('custom доходит до приёмника с именем из реестра', () => {
        const got: string[] = [];
        const sink: DialogueEffectSink = {
            giveItem: () => {},
            takeItem: () => {},
            playSound: () => {},
            showToast: () => {},
            custom: (name) => got.push(name)
        };
        applyDialogueEffect({ kind: 'custom', id: DIALOGUE_CUSTOM.plant_flowers }, sink);
        expect(got).toEqual(['plant_flowers']);
    });

    it('квест завершён, но эпилог (без doneFlag) остаётся активной стадией', () => {
        const s = new GameState();
        s.setFlag('quest_bells_taken');
        s.setFlag('quest_bells_done');
        expect(activeStage(s, bells)?.npc).toBe('trader');
    });
});