/**
 * Unit tests for macroValidator.
 */

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

// Build a macroCatalog entry
function macro(name, paramCount = 0) {
    return { name, params: Array.from({ length: paramCount }, (_, i) => `p${i}`) };
}

function makeCatalog(macros) {
    const catalog = {};
    for (const m of macros) {
        catalog[m.name.toUpperCase()] = m;
    }
    return catalog;
}

function makeCaseMap(macros) {
    const map = {};
    for (const m of macros) {
        map[m.name.toUpperCase()] = m.name;
    }
    return map;
}

// Wrap invocation lines with HASH-OFF so the classifier treats them as
// executable (not implicit output). Default hash mode is HASH-ON, so bare
// lines without a # prefix are classified as output and skipped by the
// macro validator.
function execDoc(lines) {
    return makeDocument(['HASH-OFF', ...lines]);
}

describe('validateMacros', () => {
    describe('happy path — no diagnostics', () => {
        test('returns empty array when no macros in document', () => {
            const doc = makeDocument(['SET x = 1']);
            expect(validateMacros(doc, alwaysWarn, {
                macroCatalog: {},
                macroCaseMap: {},
                macroDuplicates: []
            })).toHaveLength(0);
        });

        test('returns empty array for correct no-param macro invocation', () => {
            const macros = [macro('INIT_REPORT', 0)];
            const doc = execDoc(['INIT_REPORT']);
            expect(validateMacros(doc, alwaysWarn, {
                macroCatalog: makeCatalog(macros),
                macroCaseMap: makeCaseMap(macros),
                macroDuplicates: []
            })).toHaveLength(0);
        });

        test('returns empty array for correct parameterised macro invocation', () => {
            const macros = [macro('FORMAT_DATE', 2)];
            const doc = execDoc(['FORMAT_DATE(zDate, zFmt)']);
            expect(validateMacros(doc, alwaysWarn, {
                macroCatalog: makeCatalog(macros),
                macroCaseMap: makeCaseMap(macros),
                macroDuplicates: []
            })).toHaveLength(0);
        });
    });

    describe('ACU-MAC-001 — duplicate macro declaration', () => {
        test('flags duplicate macro declaration', () => {
            const doc = makeDocument([
                'MACRO INIT_REPORT',
                'MACRO INIT_REPORT'
            ]);
            const diags = validateMacros(doc, alwaysWarn, {
                macroCatalog: makeCatalog([macro('INIT_REPORT')]),
                macroCaseMap: makeCaseMap([macro('INIT_REPORT')]),
                macroDuplicates: [{ name: 'INIT_REPORT', lineIndexes: [0, 1] }]
            });
            expect(messageIds(diags)).toContain('ACU-MAC-001');
        });
    });

    describe('ACU-MAC-002 — macro case mismatch', () => {
        test('flags macro invoked with wrong capitalisation', () => {
            const macros = [macro('INIT_REPORT', 0)];
            const doc = execDoc(['init_report']);
            const diags = validateMacros(doc, alwaysWarn, {
                macroCatalog: makeCatalog(macros),
                macroCaseMap: makeCaseMap(macros),
                macroDuplicates: []
            });
            expect(messageIds(diags)).toContain('ACU-MAC-002');
        });
    });

    describe('ACU-MAC-003 — unexpected parameters', () => {
        test('flags no-param macro called with parentheses', () => {
            const macros = [macro('INIT_REPORT', 0)];
            const doc = execDoc(['INIT_REPORT()']);
            const diags = validateMacros(doc, alwaysWarn, {
                macroCatalog: makeCatalog(macros),
                macroCaseMap: makeCaseMap(macros),
                macroDuplicates: []
            });
            expect(messageIds(diags)).toContain('ACU-MAC-003');
        });
    });

    describe('ACU-MAC-004 — missing parameters', () => {
        test('flags parameterised macro called without parentheses', () => {
            const macros = [macro('FORMAT_DATE', 2)];
            const doc = execDoc(['FORMAT_DATE']);
            const diags = validateMacros(doc, alwaysWarn, {
                macroCatalog: makeCatalog(macros),
                macroCaseMap: makeCaseMap(macros),
                macroDuplicates: []
            });
            expect(messageIds(diags)).toContain('ACU-MAC-004');
        });
    });

    describe('severity', () => {
        test('produces no diagnostics when severity is off', () => {
            const macros = [macro('INIT_REPORT', 0)];
            const doc = execDoc(['INIT_REPORT()']);
            expect(validateMacros(doc, alwaysOff, {
                macroCatalog: makeCatalog(macros),
                macroCaseMap: makeCaseMap(macros),
                macroDuplicates: []
            })).toHaveLength(0);
        });
    });
});
