import { describe, expect, it } from 'vitest';
import { resolveInteractable, type InteractableDef, type InteractCtx } from '../interactables';

const ctx = (over: Partial<InteractCtx> = {}): InteractCtx => ({
    hasFlag: (f) => f === 'quest_bells_done',
    hasItem: (i) => i === 'cloth',
    getVar: (id) => (id === 'motes' ? 3 : 0),
    used: (id) => id === 'spent',
    ...over
});

const def = (over: Partial<InteractableDef> = {}): InteractableDef => ({
    id: 'obj',
    pos: { x: 5.5, y: 5.5 },
    tile: { x: 5, y: 5 },
    kind: 'examine',
    responses: [{ text: 'обычная' }],
    ...over
});

describe('resolveInteractable — выбор реакции', () => {
    it('без when — первая реакция', () => {
        const pick = resolveInteractable(def(), ctx());
        expect(pick?.ok && pick.response.text).toBe('обычная');
    });

    it('первая подходящая по when побеждает', () => {
        const d = def({
            responses: [
                { when: { flag: 'quest_bells_done' }, text: 'после' },
                { text: 'до' }
            ]
        });
        expect(resolveInteractable(d, ctx())?.ok && 'после').toBe('после');
        expect(resolveInteractable(d, ctx({ hasFlag: () => false }))?.ok && 'до').toBe('до');
    });

    it('notFlag и varAtLeast учитываются', () => {
        const d = def({ responses: [{ when: { notFlag: 'quest_bells_done' }, text: 'x' }] });
        expect(resolveInteractable(d, ctx())).toBeNull();
        const v = def({ responses: [{ when: { varAtLeast: { id: 'motes', value: 5 } }, text: 'x' }] });
        expect(resolveInteractable(v, ctx())).toBeNull();
        expect(resolveInteractable(v, ctx({ getVar: () => 7 }))?.ok).toBe(true);
    });
});

describe('resolveInteractable — once и requiresItem', () => {
    it('once: использованный молчит, неиспользованный реагирует', () => {
        const d = def({ id: 'chest', once: true, responses: [{ text: 'сундук' }] });
        expect(resolveInteractable(d, ctx())?.ok).toBe(true);
        expect(resolveInteractable(d, ctx({ used: () => true }))).toBeNull();
    });

    it('requiresItem без предмета — отказ; с предметом реагирует', () => {
        const d = def({
            requiresItem: 'salt',
            lockedText: 'Нужна соль.',
            responses: [{ text: 'взял' }]
        });
        expect(resolveInteractable(d, ctx())).toEqual({ ok: false, lockedText: 'Нужна соль.' });
        expect(resolveInteractable(d, ctx({ hasItem: () => true }))?.ok).toBe(true);
    });

    it('requiresItem без lockedText — отказ по умолчанию', () => {
        const pick = resolveInteractable(def({ requiresItem: 'salt' }), ctx());
        expect(pick && !pick.ok && pick.lockedText.length > 0).toBe(true);
    });

    it('once-использованный с requiresItem — отказ важнее молчания', () => {
        const d = def({ once: true, requiresItem: 'salt', responses: [{ text: 'x' }] });
        expect(resolveInteractable(d, ctx({ used: () => true }))).toMatchObject({ ok: false });
    });
});