import { describe, expect, it } from 'vitest';
import type { DialogueGraph } from '@rpg/engine';
import { analyzeGraph, checkGraph, type GraphRefs } from '../dialogueRules';

const refs = (): GraphRefs => ({
    flags: new Set(['met_elder', 'quest_bells_taken']),
    vars: new Set(['flowers']),
    items: new Set(['cloth']),
    customs: new Set(['plant_flowers']),
    strings: new Set(['line.key'])
});

describe('checkGraph — ссылки и структура', () => {
    it('чистый граф — ошибок нет', () => {
        const g: DialogueGraph = {
            start: 'a',
            nodes: { a: { text: 'Привет', next: 'b' }, b: { text: 'Пока' } }
        };
        expect(checkGraph('g', g, refs()).filter((i) => i.severity === 'error')).toEqual([]);
    });

    it('битые ссылки: флаг, вар, предмет, custom, textKey, next, start', () => {
        const g: DialogueGraph = {
            start: 'a',
            nodes: {
                a: {
                    text: 'x',
                    when: ['нет_такого'],
                    whenVars: [{ key: 'нет_вара', op: 'ge', value: 1 }],
                    hasItem: ['salt'],
                    do: [
                        { kind: 'giveItem', id: 'нет_предмета' },
                        { kind: 'custom', id: 'нет_эффекта' }
                    ],
                    textKey: 'нет.строки',
                    next: 'в_никуда'
                }
            }
        };
        const ids = checkGraph('g', g, refs()).map((i) => i.id);
        expect(ids).toContain('flag-unknown');
        expect(ids).toContain('var-unknown');
        expect(ids).toContain('item-unknown');
        expect(ids).toContain('do-item-unknown');
        expect(ids).toContain('do-custom-unknown');
        expect(ids).toContain('string-unknown');
        expect(ids).toContain('dialogue-next');
        // битый start
        const ids2 = checkGraph('g', { start: 'нет', nodes: {} }, refs()).map((i) => i.id);
        expect(ids2).toContain('dialogue-start');
    });

    it('правильные ссылки проходят, упоминания пишутся в used-множества', () => {
        const usedFlags = new Set<string>();
        const usedVars = new Set<string>();
        const g: DialogueGraph = {
            start: 'a',
            nodes: {
                a: {
                    when: ['met_elder'],
                    whenVars: [{ key: 'flowers', op: 'ge', value: 3 }],
                    setFlags: ['quest_bells_taken'],
                    do: [{ kind: 'giveItem', id: 'cloth' }, { kind: 'custom', id: 'plant_flowers' }],
                    text: 'ok'
                }
            }
        };
        expect(checkGraph('g', g, { ...refs(), usedFlags, usedVars })).toEqual([]);
        expect([...usedFlags]).toEqual(['met_elder', 'quest_bells_taken']);
        expect([...usedVars]).toEqual(['flowers']);
    });
});

describe('analyzeGraph — достижимость', () => {
    it('сироты и тихие концы', () => {
        const g: DialogueGraph = {
            start: 'a',
            nodes: {
                a: { text: 'ok', next: 'silent' },
                lost: { text: 'никто не ссылается' },
                silent: { setFlags: ['met_elder'] } // нет next и текста — молча кончается
            }
        };
        const a = analyzeGraph(g);
        expect(a.reachable).toEqual(['a', 'silent']);
        expect(a.orphans).toEqual(['lost']);
        expect(a.silentEnds).toEqual(['silent']);
    });

    it('цикл без текста — error, цикл с текстом — не цикл-ошибка', () => {
        const g: DialogueGraph = {
            start: 'a',
            nodes: {
                a: { text: 'Слово', next: 'hub' },
                hub: { next: 'hub' } // действие само в себя
            }
        };
        const a = analyzeGraph(g);
        expect(a.textlessCycles).toEqual([['hub']]);
        expect(a.silentEnds).toEqual([]);
    });

    it('выборы считаются рёбрами: цель выбора достижима', () => {
        const g: DialogueGraph = {
            start: 'q',
            nodes: {
                q: { text: '?', choices: [{ text: 'Да', next: 'yes' }] },
                yes: { text: 'Да' }
            }
        };
        const a = analyzeGraph(g);
        expect(a.orphans).toEqual([]);
        expect(a.reachable).toEqual(['q', 'yes']);
    });
});