const assert = require('node:assert/strict');
const fs = require('node:fs');
const test = require('node:test');

const oniguruma = require('vscode-oniguruma');
const textmate = require('vscode-textmate');

async function loadGrammar() {
    await oniguruma.loadWASM(fs.readFileSync(require.resolve('vscode-oniguruma/release/onig.wasm')).buffer);

    const registry = new textmate.Registry({
        onigLib: Promise.resolve({
            createOnigScanner: patterns => new oniguruma.OnigScanner(patterns),
            createOnigString: value => new oniguruma.OnigString(value)
        }),
        loadGrammar: async scopeName => scopeName === 'source.eftl'
            ? textmate.parseRawGrammar(
                fs.readFileSync('syntaxes/eftl.tmLanguage.json', 'utf8'),
                'eftl.tmLanguage.json'
            )
            : null
    });

    return registry.loadGrammar('source.eftl');
}

test('highlights nested VAR blocks, self-closing functions, and WHILE attributes', async () => {
    const grammar = await loadGrammar();
    const source = [
        '[EFTL]',
        '[VAR name="count" type="number"][SIZE_OF varname="items" /][/VAR]',
        '[WHILE threshold="99"][CONDITION][% count > 0 %][/CONDITION][DO]',
        '[VAR name="current" type="string"][VALUE_OF varname="items" index="count" /][/VAR]',
        '[/DO][/WHILE]',
        '[IF][CONDITION][% count == 0 %][/CONDITION][THEN]',
        '[VAR name="empty" type="boolean"][% empty = true; %][/VAR]',
        '[/THEN][/IF]',
        '[/EFTL]'
    ];
    let ruleStack = textmate.INITIAL;
    const variableTokens = [];
    const selfClosingFunctionTokens = [];
    const thresholdTokens = [];
    const thresholdValueTokens = [];
    const whileTokens = [];

    for (const line of source) {
        const result = grammar.tokenizeLine(line, ruleStack);
        ruleStack = result.ruleStack;

        for (const token of result.tokens) {
            const text = line.slice(token.startIndex, token.endIndex);
            if (text === 'VAR' && line[token.startIndex - 1] !== '/') variableTokens.push(token);
            if (text === 'SIZE_OF' || text === 'VALUE_OF') selfClosingFunctionTokens.push(token);
            if (text === 'threshold') thresholdTokens.push(token);
            if (text === '"99"') thresholdValueTokens.push(token);
            if (text === 'WHILE') whileTokens.push(token);
        }
    }

    assert.equal(variableTokens.length, 3);
    for (const token of variableTokens) {
        assert.ok(token.scopes.includes('storage.type.variable.eftl'));
    }

    for (const token of selfClosingFunctionTokens) {
        assert.ok(token.scopes.includes('meta.function.eftl'));
        assert.ok(!token.scopes.includes('meta.function.block.eftl'));
    }

    assert.equal(thresholdTokens.length, 1);
    assert.ok(thresholdTokens[0].scopes.includes('entity.other.attribute-name.eftl'));
    assert.equal(thresholdValueTokens.length, 1);
    assert.ok(thresholdValueTokens[0].scopes.includes('string.quoted.double.eftl'));
    assert.equal(whileTokens.length, 1);
    assert.ok(whileTokens[0].scopes.includes('keyword.control.loop.eftl'));
});