/**
 * Unit tests for collectDiagnostics.
 *
 * collectDiagnostics is the validation orchestrator — it delegates to individual
 * validators and applies the suppression filter. These tests focus on behaviours
 * owned by collectDiagnostics itself:
 *   - returns an array
 *   - end-to-end: known error produces the expected messageId
 *   - suppression filter: @rule-ignore removes a diagnostic
 *   - severity gate: getRuleSeverity returning null omits a rule's diagnostics
 */

import { collectDiagnostics } from "../../core/src/collectDiagnostics.js";
import * as vscode from "../__mocks__/vscode.js";
import { getProfileRuleSeverities } from "../../core/src/diagnostics/diagnosticCatalog.js";

let docSeq = 0;

/**
 * Creates a minimal document with all properties required by collectDiagnostics
 * and its dependencies (collectDocumentFacts, suppressionParser, etc.).
 * Uses no INCLUDE or REQUIRE directives to avoid filesystem I/O.
 */
function makeDoc(lines) {
    docSeq += 1;
    const fsPath = `/test/collect-${docSeq}.txt`;
    return {
        lineCount: lines.length,
        lineAt: (i) => ({ text: lines[i] }),
        uri: { fsPath, toString: () => `file://${fsPath}` },
        fileName: fsPath,
        version: docSeq
    };
}

/** getRuleSeverity that maps the default profile severity string to vscode values. */
function defaultSeverity(ruleId) {
    const map = { error: vscode.DiagnosticSeverity.Error, warning: vscode.DiagnosticSeverity.Warning, information: vscode.DiagnosticSeverity.Information };
    const sev = getProfileRuleSeverities('default')[ruleId];
    return (sev && sev !== 'off') ? (map[sev] ?? null) : null;
}

/** getRuleSeverity that suppresses every rule. */
function allOff() { return null; }

describe('collectDiagnostics', () => {
    describe('return shape', () => {
        test('returns an array', () => {
            const result = collectDiagnostics(makeDoc(['']), allOff);
            expect(Array.isArray(result)).toBe(true);
        });

        test('returns empty array for a blank document with all rules off', () => {
            const result = collectDiagnostics(makeDoc(['']), allOff);
            expect(result).toHaveLength(0);
        });
    });

    describe('end-to-end diagnostic production', () => {
        // Use #HASH-OFF so lines without >> prefix are classified as executable.
        // In HASH-ON mode (the Architect default), bare lines are classified as
        // output and skipped by validateBlockStructure.
        test('unclosed IF block produces ACU-BLK-003', () => {
            const doc = makeDoc(['#HASH-OFF', 'IF x = 1', 'SET y = 2']);
            const diags = collectDiagnostics(doc, defaultSeverity);
            const ids = diags.map((d) => d.data?.messageId);
            expect(ids).toContain('ACU-BLK-003');
        });

        test('stray ENDIF produces ACU-BLK-001', () => {
            const doc = makeDoc(['#HASH-OFF', 'ENDIF']);
            const diags = collectDiagnostics(doc, defaultSeverity);
            const ids = diags.map((d) => d.data?.messageId);
            expect(ids).toContain('ACU-BLK-001');
        });
    });

    describe('severity gate', () => {
        test('getRuleSeverity returning null for blockStructure omits block diagnostics', () => {
            const doc = makeDoc(['#HASH-OFF', 'IF x = 1', 'SET y = 2']);
            const noBlock = (ruleId) => ruleId === 'blockStructure' ? null : defaultSeverity(ruleId);
            const diags = collectDiagnostics(doc, noBlock);
            const ids = diags.map((d) => d.data?.messageId);
            expect(ids).not.toContain('ACU-BLK-003');
        });
    });

    describe('suppression filter', () => {
        test('@rule-ignore on preceding line removes the matching diagnostic', () => {
            // Line 0: #HASH-OFF  (switches to executable mode)
            // Line 1: suppression comment targeting line 2
            // Line 2: IF with no ENDIF → ACU-BLK-003 at line 2, suppressed
            const doc = makeDoc([
                '#HASH-OFF',
                '// @rule-ignore ACU-BLK-003',
                'IF x = 1'
            ]);
            const diags = collectDiagnostics(doc, defaultSeverity);
            const ids = diags.map((d) => d.data?.messageId);
            expect(ids).not.toContain('ACU-BLK-003');
        });

        test('@rule-ignore for a different ID does not remove the diagnostic', () => {
            const doc = makeDoc([
                '#HASH-OFF',
                '// @rule-ignore ACU-VAR-001',
                'IF x = 1'
            ]);
            const diags = collectDiagnostics(doc, defaultSeverity);
            const ids = diags.map((d) => d.data?.messageId);
            expect(ids).toContain('ACU-BLK-003');
        });
    });
});
