import { describe, expect, it } from 'vitest';
import { resolveTransition, type TransitionCtx } from '../transitions';
import type { TransitionDef } from '../locations';

const ctx: TransitionCtx = {
    hasFlag: (f) => f === 'door_open',
    hasItem: (i) => i === 'salt'
};

const at = (x: number, y: number) => ({ x, y });

describe('resolveTransition — выбор по тайлу/триггеру', () => {
    const defs: TransitionDef[] = [
        { tile: at(2, 2), target: { kind: 'area', area: 'ponds', entry: at(3, 3) } },
        { tile: at(5, 5), label: 'Дверь', target: { kind: 'area', area: 'zvenets', entry: at(1, 1) }, trigger: 'click' },
        { tile: at(9, 6), target: { kind: 'return' }, trigger: 'click' }
    ];

    it('step-переход: наступил на тайл — сработал', () => {
        const pick = resolveTransition(defs, at(2, 2), 'step', ctx);
        expect(pick?.ok).toBe(true);
    });

    it('step не срабатывает на click-переходе того же тайла', () => {
        expect(resolveTransition(defs, at(5, 5), 'step', ctx)).toBeNull();
        expect(resolveTransition(defs, at(5, 5), 'click', ctx)?.ok).toBe(true);
    });

    it('клик мимо переходов — null', () => {
        expect(resolveTransition(defs, at(20, 20), 'click', ctx)).toBeNull();
    });

    it('return-переход выбирается по kind цели, не по тайлу входа', () => {
        const pick = resolveTransition(defs, at(9, 6), 'click', ctx);
        expect(pick?.ok && pick.def.target.kind === 'return').toBe(true);
    });
});

describe('resolveTransition — условия и отказ', () => {
    it('requiresFlag не выполнен — отказ с lockedText', () => {
        const defs: TransitionDef[] = [
            {
                tile: at(4, 4),
                target: { kind: 'area', area: 'zvenets', entry: at(1, 1) },
                requiresFlag: 'gate_open',
                lockedText: 'Заперто.'
            }
        ];
        const pick = resolveTransition(defs, at(4, 4), 'step', ctx);
        expect(pick).toEqual({ ok: false, lockedText: 'Заперто.' });
        // Флаг поставлен — пускает.
        expect(resolveTransition(defs, at(4, 4), 'step', { hasFlag: () => true, hasItem: () => false })?.ok).toBe(true);
    });

    it('requiresItem не выполнен — отказ; с предметом пускает', () => {
        const defs: TransitionDef[] = [
            {
                tile: at(4, 4),
                target: { kind: 'area', area: 'zvenets', entry: at(1, 1) },
                requiresItem: 'salt',
                lockedText: 'Нужна соль.'
            }
        ];
        expect(resolveTransition(defs, at(4, 4), 'step', { hasFlag: () => false, hasItem: () => false })).toEqual({
            ok: false,
            lockedText: 'Нужна соль.'
        });
        expect(resolveTransition(defs, at(4, 4), 'step', ctx)?.ok).toBe(true);
    });

    it('без lockedText — текст отказа по умолчанию', () => {
        const pick = resolveTransition(
            [{ tile: at(4, 4), target: { kind: 'area', area: 'zvenets', entry: at(1, 1) }, requiresFlag: 'x' }],
            at(4, 4),
            'step',
            ctx
        );
        expect(pick?.ok).toBe(false);
        expect(pick && !pick.ok && pick.lockedText.length > 0).toBe(true);
    });
});

describe('resolveTransition — дисарм входа', () => {
    const defs: TransitionDef[] = [
        { tile: at(3, 3), target: { kind: 'area', area: 'meadows', entry: at(9, 9) } }
    ];

    it('step-переход на тайле входа игнорируется, пока герой не покинул его', () => {
        expect(resolveTransition(defs, at(3, 3), 'step', ctx, at(3, 3))).toBeNull();
    });

    it('после ухода с тайла входа переход снова работает', () => {
        expect(resolveTransition(defs, at(3, 3), 'step', ctx, at(10, 10))?.ok).toBe(true);
        expect(resolveTransition(defs, at(3, 3), 'step', ctx, null)?.ok).toBe(true);
    });

    it('дисарм не глушит click-переходы на том же тайле', () => {
        const clickDefs: TransitionDef[] = [
            { tile: at(3, 3), target: { kind: 'return' }, trigger: 'click' }
        ];
        expect(resolveTransition(clickDefs, at(3, 3), 'click', ctx, at(3, 3))?.ok).toBe(true);
    });
});