"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.isSuppressionPathMatch = isSuppressionPathMatch;
exports.findMatchingSuppression = findMatchingSuppression;
exports.readValidationSuppressions = readValidationSuppressions;
exports.getSuppressionsFilePathLabel = getSuppressionsFilePathLabel;
exports.getSuppressionScopeLabel = getSuppressionScopeLabel;
const path = __importStar(require("node:path"));
const node_fs_1 = require("node:fs");
const suppressionsRelativePath = '.archpilot/suppressions.json';
function normalizePath(value) {
    let normalized = value.replace(/\\/g, '/').trim();
    while (normalized.startsWith('./')) {
        normalized = normalized.slice(2);
    }
    return normalized;
}
function toDeterministicSuppressionConfigError(message) {
    return {
        id: 'AP-ARCH-009',
        severity: 'error',
        passed: false,
        message,
    };
}
function escapeRegex(value) {
    return value.replace(/[.+?^${}()|[\]\\]/g, '\\$&');
}
function globToRegex(pattern) {
    let expression = '^';
    for (let index = 0; index < pattern.length; index += 1) {
        const char = pattern[index];
        const nextChar = index + 1 < pattern.length ? pattern[index + 1] : '';
        if (char === '*' && nextChar === '*') {
            expression += '.*';
            index += 1;
            continue;
        }
        if (char === '*') {
            expression += '[^/]*';
            continue;
        }
        expression += escapeRegex(char);
    }
    expression += '$';
    return new RegExp(expression, 'u');
}
function isSuppressionPathMatch(pattern, findingPath) {
    const normalizedPattern = normalizePath(pattern);
    const normalizedFindingPath = normalizePath(findingPath);
    if (!normalizedPattern || !normalizedFindingPath) {
        return false;
    }
    if (!normalizedPattern.includes('*')) {
        return normalizedPattern === normalizedFindingPath;
    }
    if (normalizedPattern.endsWith('/**')) {
        const prefix = normalizedPattern.slice(0, -3);
        return (normalizedFindingPath === prefix ||
            normalizedFindingPath.startsWith(`${prefix}/`));
    }
    return globToRegex(normalizedPattern).test(normalizedFindingPath);
}
function findMatchingSuppression(ruleId, findingPath, suppressions) {
    for (const suppression of suppressions) {
        if (suppression.ruleId !== ruleId) {
            continue;
        }
        if (!suppression.scope) {
            return suppression;
        }
        if (findingPath && isSuppressionPathMatch(suppression.scope, findingPath)) {
            return suppression;
        }
    }
    return undefined;
}
async function readValidationSuppressions(workspaceRoot, knownRuleIds) {
    const suppressionsPath = path.join(workspaceRoot, '.archpilot', 'suppressions.json');
    let contents;
    try {
        contents = await node_fs_1.promises.readFile(suppressionsPath, { encoding: 'utf8' });
    }
    catch {
        return {
            suppressions: [],
            ignored: [],
            configResults: [],
        };
    }
    let parsed;
    try {
        parsed = JSON.parse(contents);
    }
    catch {
        return {
            suppressions: [],
            ignored: [],
            configResults: [
                toDeterministicSuppressionConfigError('ArchPilot suppression configuration is invalid JSON. Please fix .archpilot/suppressions.json.'),
            ],
        };
    }
    if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
        return {
            suppressions: [],
            ignored: [],
            configResults: [
                toDeterministicSuppressionConfigError('ArchPilot suppression configuration is invalid: .archpilot/suppressions.json must be a JSON object with a suppressions array.'),
            ],
        };
    }
    if (!Array.isArray(parsed.suppressions)) {
        return {
            suppressions: [],
            ignored: [],
            configResults: [
                toDeterministicSuppressionConfigError('ArchPilot suppression configuration is invalid: .archpilot/suppressions.json must define a suppressions array.'),
            ],
        };
    }
    const suppressions = [];
    const ignored = [];
    for (let index = 0; index < parsed.suppressions.length; index += 1) {
        const rawEntry = parsed.suppressions[index];
        if (!rawEntry || typeof rawEntry !== 'object' || Array.isArray(rawEntry)) {
            ignored.push({
                index,
                reason: 'Suppression entry must be an object.',
            });
            continue;
        }
        const ruleId = typeof rawEntry.ruleId === 'string' ? rawEntry.ruleId : undefined;
        const entryScopeValue = typeof rawEntry.scope === 'string'
            ? rawEntry.scope
            : typeof rawEntry.path === 'string'
                ? rawEntry.path
                : undefined;
        const reason = typeof rawEntry.reason === 'string' ? rawEntry.reason.trim() : undefined;
        const expiresOn = typeof rawEntry.expiresOn === 'string' ? rawEntry.expiresOn.trim() : undefined;
        if (!ruleId) {
            ignored.push({
                index,
                scope: entryScopeValue,
                reason: 'Suppression entry is missing ruleId.',
                ...(expiresOn ? { expiresOn } : {}),
            });
            continue;
        }
        if (!knownRuleIds.has(ruleId)) {
            ignored.push({
                index,
                ruleId,
                ...(entryScopeValue ? { scope: normalizePath(entryScopeValue) } : {}),
                reason: 'Unknown rule ID. Entry was ignored.',
                ...(expiresOn ? { expiresOn } : {}),
            });
            continue;
        }
        suppressions.push({
            index,
            ruleId: ruleId,
            ...(entryScopeValue ? { scope: normalizePath(entryScopeValue) } : {}),
            ...(reason ? { reason } : {}),
            ...(expiresOn ? { expiresOn } : {}),
        });
    }
    return {
        suppressions: suppressions.sort((left, right) => {
            const ruleCompare = left.ruleId.localeCompare(right.ruleId);
            if (ruleCompare !== 0) {
                return ruleCompare;
            }
            const scopeCompare = (left.scope ?? '').localeCompare(right.scope ?? '');
            if (scopeCompare !== 0) {
                return scopeCompare;
            }
            const reasonCompare = (left.reason ?? '').localeCompare(right.reason ?? '');
            if (reasonCompare !== 0) {
                return reasonCompare;
            }
            const expiryCompare = (left.expiresOn ?? '').localeCompare(right.expiresOn ?? '');
            if (expiryCompare !== 0) {
                return expiryCompare;
            }
            return left.index - right.index;
        }),
        ignored: ignored.sort((left, right) => left.index - right.index),
        configResults: [],
    };
}
function getSuppressionsFilePathLabel() {
    return suppressionsRelativePath;
}
function getSuppressionScopeLabel(entry) {
    return entry.scope ?? '(rule-wide)';
}
//# sourceMappingURL=validationSuppressions.js.map