"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.getExceptionsFilePath = getExceptionsFilePath;
exports.buildDefaultExceptionsTemplate = buildDefaultExceptionsTemplate;
exports.ensureExceptionsFile = ensureExceptionsFile;
exports.exceptionsFileExists = exceptionsFileExists;
exports.loadExceptions = loadExceptions;
exports.isExceptionExpired = isExceptionExpired;
exports.findMatchingException = findMatchingException;
exports.shouldIgnoreViolationDueToException = shouldIgnoreViolationDueToException;
const path = __importStar(require("node:path"));
const node_fs_1 = require("node:fs");
const exceptionsFileRelativePath = '.archpilot/exceptions.json';
function normalizeExceptionEntry(value) {
    if (!value || typeof value !== 'object' || Array.isArray(value)) {
        return undefined;
    }
    const typed = value;
    if (typeof typed.ruleId !== 'string' ||
        typeof typed.reason !== 'string' ||
        typeof typed.approvedBy !== 'string' ||
        typeof typed.createdAt !== 'string') {
        return undefined;
    }
    return {
        ruleId: typed.ruleId.trim(),
        ...(typeof typed.module === 'string' && typed.module.trim().length > 0
            ? { module: typed.module.trim() }
            : {}),
        ...(typeof typed.target === 'string' && typed.target.trim().length > 0
            ? { target: typed.target.trim() }
            : {}),
        ...(typeof typed.filePath === 'string' && typed.filePath.trim().length > 0
            ? { filePath: typed.filePath.trim() }
            : {}),
        ...(typeof typed.table === 'string' && typed.table.trim().length > 0
            ? { table: typed.table.trim() }
            : {}),
        ...(typeof typed.api === 'string' && typed.api.trim().length > 0
            ? { api: typed.api.trim() }
            : {}),
        reason: typed.reason.trim(),
        approvedBy: typed.approvedBy.trim(),
        createdAt: typed.createdAt.trim(),
        ...(typeof typed.expiresAt === 'string' && typed.expiresAt.trim().length > 0
            ? { expiresAt: typed.expiresAt.trim() }
            : {}),
    };
}
function normalizeExceptions(raw) {
    if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
        return undefined;
    }
    const typed = raw;
    if (!Array.isArray(typed.exceptions)) {
        return undefined;
    }
    return typed.exceptions
        .map((entry) => normalizeExceptionEntry(entry))
        .filter((entry) => !!entry)
        .sort((left, right) => {
        const ruleCompare = left.ruleId.localeCompare(right.ruleId);
        if (ruleCompare !== 0) {
            return ruleCompare;
        }
        const moduleCompare = (left.module ?? '').localeCompare(right.module ?? '');
        if (moduleCompare !== 0) {
            return moduleCompare;
        }
        const targetCompare = (left.target ?? '').localeCompare(right.target ?? '');
        if (targetCompare !== 0) {
            return targetCompare;
        }
        const filePathCompare = (left.filePath ?? '').localeCompare(right.filePath ?? '');
        if (filePathCompare !== 0) {
            return filePathCompare;
        }
        const tableCompare = (left.table ?? '').localeCompare(right.table ?? '');
        if (tableCompare !== 0) {
            return tableCompare;
        }
        const apiCompare = (left.api ?? '').localeCompare(right.api ?? '');
        if (apiCompare !== 0) {
            return apiCompare;
        }
        const expiryCompare = (left.expiresAt ?? '').localeCompare(right.expiresAt ?? '');
        if (expiryCompare !== 0) {
            return expiryCompare;
        }
        return left.reason.localeCompare(right.reason);
    });
}
function normalizeToken(value) {
    return value.trim().toLowerCase();
}
function normalizePathToken(value) {
    return value.replace(/\\/g, '/').trim().toLowerCase();
}
function buildExceptionMatchContext(result) {
    return {
        ruleId: result.id,
        ...(result.module ? { module: result.module } : {}),
        ...(result.target ? { target: result.target } : {}),
        ...(result.filePath
            ? { filePath: result.filePath }
            : result.sourceFile
                ? { filePath: result.sourceFile }
                : {}),
        ...(result.table ? { table: result.table } : {}),
        ...(result.api ? { api: result.api } : {}),
    };
}
function getExceptionsFilePath(workspaceRoot) {
    return path.join(workspaceRoot, '.archpilot', 'exceptions.json');
}
function buildDefaultExceptionsTemplate() {
    return `${JSON.stringify({ exceptions: [] }, null, 2)}\n`;
}
async function ensureExceptionsFile(workspaceRoot) {
    const exceptionsPath = getExceptionsFilePath(workspaceRoot);
    await node_fs_1.promises.mkdir(path.dirname(exceptionsPath), { recursive: true });
    try {
        await node_fs_1.promises.access(exceptionsPath);
        return { path: exceptionsPath, created: false };
    }
    catch {
        // File missing; create deterministic template.
    }
    await node_fs_1.promises.writeFile(exceptionsPath, buildDefaultExceptionsTemplate(), { encoding: 'utf8' });
    return { path: exceptionsPath, created: true };
}
async function exceptionsFileExists(workspaceRoot) {
    try {
        await node_fs_1.promises.access(getExceptionsFilePath(workspaceRoot));
        return true;
    }
    catch {
        return false;
    }
}
async function loadExceptions(workspaceRoot) {
    const exceptionsPath = getExceptionsFilePath(workspaceRoot);
    let content;
    try {
        content = await node_fs_1.promises.readFile(exceptionsPath, { encoding: 'utf8' });
    }
    catch (error) {
        if (error.code === 'ENOENT') {
            return { exceptions: [], source: 'default' };
        }
        return {
            exceptions: [],
            source: 'fallback-default',
            configError: error instanceof Error
                ? `ArchPilot exceptions configuration could not be read. ${error.message}`
                : `ArchPilot exceptions configuration could not be read. Please verify ${exceptionsFileRelativePath}.`,
        };
    }
    let parsed;
    try {
        parsed = JSON.parse(content);
    }
    catch {
        return {
            exceptions: [],
            source: 'fallback-default',
            configError: 'ArchPilot exceptions configuration is invalid JSON. Please fix .archpilot/exceptions.json.',
        };
    }
    const normalized = normalizeExceptions(parsed);
    if (!normalized) {
        return {
            exceptions: [],
            source: 'fallback-default',
            configError: 'ArchPilot exceptions configuration is invalid: .archpilot/exceptions.json must define an exceptions array.',
        };
    }
    return {
        exceptions: normalized,
        source: 'exceptions-file',
    };
}
function isExceptionExpired(exception, nowIso = new Date().toISOString()) {
    if (!exception.expiresAt) {
        return false;
    }
    const expiresAtMs = Date.parse(exception.expiresAt);
    const nowMs = Date.parse(nowIso);
    if (Number.isNaN(expiresAtMs) || Number.isNaN(nowMs)) {
        return false;
    }
    return expiresAtMs < nowMs;
}
function findMatchingException(context, exceptions) {
    return exceptions.find((entry) => {
        if (entry.ruleId !== context.ruleId) {
            return false;
        }
        if (isExceptionExpired(entry)) {
            return false;
        }
        if (entry.module) {
            if (!context.module) {
                return false;
            }
            if (normalizeToken(entry.module) !== normalizeToken(context.module)) {
                return false;
            }
        }
        if (entry.target) {
            if (!context.target) {
                return false;
            }
            if (normalizeToken(entry.target) !== normalizeToken(context.target)) {
                return false;
            }
        }
        if (entry.filePath) {
            if (!context.filePath) {
                return false;
            }
            if (normalizePathToken(entry.filePath) !== normalizePathToken(context.filePath)) {
                return false;
            }
        }
        if (entry.table) {
            if (!context.table) {
                return false;
            }
            if (normalizeToken(entry.table) !== normalizeToken(context.table)) {
                return false;
            }
        }
        if (entry.api) {
            if (!context.api) {
                return false;
            }
            if (normalizeToken(entry.api) !== normalizeToken(context.api)) {
                return false;
            }
        }
        return true;
    });
}
function shouldIgnoreViolationDueToException(result, exceptions) {
    if (result.passed) {
        return undefined;
    }
    const context = buildExceptionMatchContext(result);
    return findMatchingException(context, exceptions);
}
//# sourceMappingURL=exceptionLoader.js.map