/**
 * Unit tests for typeValidator.
 *
 * Documents use #HASH-OFF so that bare lines (without # prefix) are treated as
 * code by the lexer. HASH-ON mode (the default) treats prefix-less lines as
 * output text, which would mean no assignment nodes reach the validator.
 */

import { validateTypes } from "../../../core/src/validators/typeValidator.js";
import { makeDocument, alwaysWarn, alwaysOff, messageIds } from "./helpers.js";

function varTypes(entries) {
    return new Map(entries.map(([name, type]) => [`global:${name}`, { name, type }]));
}

describe('validateTypes', () => {
    describe('happy path — no diagnostics', () => {
        test('returns empty array when assignment types are compatible', () => {
            const doc = makeDocument(['#HASH-OFF', 'zName = "hello"']);
            const variables = varTypes([['zName', 'STRING']]);
            expect(validateTypes(doc, alwaysWarn, { variableTypes: variables })).toHaveLength(0);
        });

        test('returns empty array when target variable is not declared (handled elsewhere)', () => {
            const doc = makeDocument(['#HASH-OFF', 'zUndeclared = "hello"']);
            const variables = varTypes([]);
            expect(validateTypes(doc, alwaysWarn, { variableTypes: variables })).toHaveLength(0);
        });

        test('returns empty array for non-assignment lines', () => {
            const doc = makeDocument(['#HASH-OFF', 'IF x = 1']);
            const variables = varTypes([['x', 'SHORT']]);
            expect(validateTypes(doc, alwaysWarn, { variableTypes: variables })).toHaveLength(0);
        });

        test('returns empty array when both operand types are known and compatible', () => {
            const doc = makeDocument(['#HASH-OFF', 'sCount = sA + sB']);
            const variables = varTypes([['sCount', 'SHORT'], ['sA', 'SHORT'], ['sB', 'SHORT']]);
            expect(validateTypes(doc, alwaysWarn, { variableTypes: variables })).toHaveLength(0);
        });
    });

    describe('ACU-TYPE-002 — type mismatch', () => {
        test('flags assigning a string literal to a SHORT variable', () => {
            const doc = makeDocument(['#HASH-OFF', 'sCount = "hello"']);
            const variables = varTypes([['sCount', 'SHORT']]);
            const diags = validateTypes(doc, alwaysWarn, { variableTypes: variables });
            expect(messageIds(diags)).toContain('ACU-TYPE-002');
        });

        test('flags assigning a numeric literal to a STRING variable', () => {
            const doc = makeDocument(['#HASH-OFF', 'zName = 42']);
            const variables = varTypes([['zName', 'STRING']]);
            const diags = validateTypes(doc, alwaysWarn, { variableTypes: variables });
            expect(messageIds(diags)).toContain('ACU-TYPE-002');
        });

        test('diagnostic message identifies the target variable', () => {
            const doc = makeDocument(['#HASH-OFF', 'sCount = "hello"']);
            const variables = varTypes([['sCount', 'SHORT']]);
            const diags = validateTypes(doc, alwaysWarn, { variableTypes: variables });
            expect(diags[0].message).toMatch(/sCount/);
        });

        test('flags assigning a string literal to a BIGINT variable', () => {
            const doc = makeDocument(['#HASH-OFF', 'lBig = "hello"']);
            const variables = varTypes([['lBig', 'BIGINT']]);
            const diags = validateTypes(doc, alwaysWarn, { variableTypes: variables });
            expect(messageIds(diags)).toContain('ACU-TYPE-002');
        });
    });

    describe('BIGINT type compatibility', () => {
        test('BIGINT variable accepts a numeric literal without diagnostic', () => {
            const doc = makeDocument(['#HASH-OFF', 'lBig = 42']);
            const variables = varTypes([['lBig', 'BIGINT']]);
            expect(validateTypes(doc, alwaysWarn, { variableTypes: variables })).toHaveLength(0);
        });

        test('BIGINT variable accepts a LONG value without diagnostic', () => {
            const doc = makeDocument(['#HASH-OFF', 'lBig = lOther']);
            const variables = varTypes([['lBig', 'BIGINT'], ['lOther', 'LONG']]);
            expect(validateTypes(doc, alwaysWarn, { variableTypes: variables })).toHaveLength(0);
        });
    });

    describe('severity', () => {
        test('produces no diagnostic when severity is off', () => {
            const doc = makeDocument(['#HASH-OFF', 'sCount = "hello"']);
            const variables = varTypes([['sCount', 'SHORT']]);
            expect(validateTypes(doc, alwaysOff, { variableTypes: variables })).toHaveLength(0);
        });
    });

    describe('executionClassificationMap option', () => {
        test('skips lines the map marks as output even when lexed as code', () => {
            // Line 0: #HASH-OFF directive; line 1: an assignment that would normally
            // fire ACU-TYPE-002 — but the ECM marks line 1 as output, so it is skipped.
            const doc = makeDocument(['#HASH-OFF', 'sCount = "hello"']);
            const variables = varTypes([['sCount', 'SHORT']]);
            const options = {
                executionClassificationMap: [{ isOutput: false }, { isOutput: true }]
            };
            expect(validateTypes(doc, alwaysWarn, { variableTypes: variables, ...options })).toHaveLength(0);
        });
    });
});
