"use strict";
/**
 * Suppression Config Loader
 * Loads and validates suppression configuration from various file formats
 */
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.findConfigFile = findConfigFile;
exports.loadConfigFromPath = loadConfigFromPath;
exports.loadSuppressionConfig = loadSuppressionConfig;
exports.writeSuppressionConfig = writeSuppressionConfig;
exports.addFindingSuppression = addFindingSuppression;
exports.removeFindingSuppression = removeFindingSuppression;
exports.addRuleSuppression = addRuleSuppression;
exports.listSuppressions = listSuppressions;
exports.isExpired = isExpired;
const fs_1 = require("fs");
const path_1 = require("path");
const yaml = __importStar(require("js-yaml"));
const types_1 = require("./types");
/**
 * Find the config file by walking up the directory tree
 */
function findConfigFile(startPath) {
    let currentDir = startPath;
    // Walk up directory tree looking for config file
    while (currentDir !== (0, path_1.dirname)(currentDir)) {
        for (const filename of types_1.SUPPRESSION_CONFIG_FILES) {
            const configPath = (0, path_1.join)(currentDir, filename);
            if ((0, fs_1.existsSync)(configPath)) {
                return configPath;
            }
        }
        currentDir = (0, path_1.dirname)(currentDir);
    }
    // Check root directory one more time
    for (const filename of types_1.SUPPRESSION_CONFIG_FILES) {
        const configPath = (0, path_1.join)(currentDir, filename);
        if ((0, fs_1.existsSync)(configPath)) {
            return configPath;
        }
    }
    return null;
}
/**
 * Parse config file content based on extension
 */
function parseConfigContent(content, filePath) {
    const isYaml = filePath.endsWith('.yaml') || filePath.endsWith('.yml');
    if (isYaml) {
        return yaml.load(content);
    }
    // JSON or .oculumrc (treat as JSON)
    return JSON.parse(content);
}
/**
 * Validate suppression config structure
 */
function validateConfig(data) {
    const errors = [];
    if (!data || typeof data !== 'object') {
        return { valid: false, errors: ['Config must be an object'] };
    }
    const config = data;
    // Validate version
    if (config.version !== undefined && config.version !== 1) {
        errors.push(`Unsupported config version: ${config.version}. Only version 1 is supported.`);
    }
    // Validate suppressions
    if (config.suppressions !== undefined) {
        if (typeof config.suppressions !== 'object' || config.suppressions === null) {
            errors.push('suppressions must be an object');
        }
        else {
            const suppressions = config.suppressions;
            // Validate rules
            if (suppressions.rules !== undefined) {
                if (!Array.isArray(suppressions.rules)) {
                    errors.push('suppressions.rules must be an array');
                }
                else {
                    suppressions.rules.forEach((rule, i) => {
                        if (!rule || typeof rule !== 'object') {
                            errors.push(`suppressions.rules[${i}] must be an object`);
                        }
                        else {
                            const r = rule;
                            if (!r.category || typeof r.category !== 'string') {
                                errors.push(`suppressions.rules[${i}].category is required`);
                            }
                            if (!r.reason || typeof r.reason !== 'string') {
                                errors.push(`suppressions.rules[${i}].reason is required`);
                            }
                            if (r.expires && typeof r.expires !== 'string') {
                                errors.push(`suppressions.rules[${i}].expires must be a string (ISO date)`);
                            }
                            if (r.paths && !Array.isArray(r.paths)) {
                                errors.push(`suppressions.rules[${i}].paths must be an array`);
                            }
                        }
                    });
                }
            }
            // Validate findings
            if (suppressions.findings !== undefined) {
                if (!Array.isArray(suppressions.findings)) {
                    errors.push('suppressions.findings must be an array');
                }
                else {
                    suppressions.findings.forEach((finding, i) => {
                        if (!finding || typeof finding !== 'object') {
                            errors.push(`suppressions.findings[${i}] must be an object`);
                        }
                        else {
                            const f = finding;
                            if (!f.hash || typeof f.hash !== 'string') {
                                errors.push(`suppressions.findings[${i}].hash is required`);
                            }
                            if (!f.file || typeof f.file !== 'string') {
                                errors.push(`suppressions.findings[${i}].file is required`);
                            }
                            if (!f.reason || typeof f.reason !== 'string') {
                                errors.push(`suppressions.findings[${i}].reason is required`);
                            }
                            if (f.expires && typeof f.expires !== 'string') {
                                errors.push(`suppressions.findings[${i}].expires must be a string (ISO date)`);
                            }
                        }
                    });
                }
            }
        }
    }
    // Validate ignore patterns
    if (config.ignore !== undefined) {
        if (!Array.isArray(config.ignore)) {
            errors.push('ignore must be an array of strings');
        }
        else {
            config.ignore.forEach((pattern, i) => {
                if (typeof pattern !== 'string') {
                    errors.push(`ignore[${i}] must be a string`);
                }
            });
        }
    }
    return { valid: errors.length === 0, errors };
}
/**
 * Load suppression config from a specific path
 */
function loadConfigFromPath(configPath) {
    const errors = [];
    try {
        const content = (0, fs_1.readFileSync)(configPath, 'utf-8');
        const data = parseConfigContent(content, configPath);
        const validation = validateConfig(data);
        if (!validation.valid) {
            return {
                config: types_1.DEFAULT_SUPPRESSION_CONFIG,
                configPath,
                found: true,
                errors: validation.errors,
            };
        }
        // Cast to config type (after validation)
        const config = data;
        // Ensure defaults
        return {
            config: {
                version: config.version || 1,
                suppressions: {
                    rules: config.suppressions?.rules || [],
                    findings: config.suppressions?.findings || [],
                },
                ignore: config.ignore || [],
            },
            configPath,
            found: true,
            errors,
        };
    }
    catch (err) {
        const message = err instanceof Error ? err.message : String(err);
        return {
            config: types_1.DEFAULT_SUPPRESSION_CONFIG,
            configPath,
            found: true,
            errors: [`Failed to parse config: ${message}`],
        };
    }
}
/**
 * Load suppression config from a project directory
 * Searches up the directory tree for a config file
 */
function loadSuppressionConfig(projectPath) {
    const configPath = findConfigFile(projectPath);
    if (!configPath) {
        return {
            config: types_1.DEFAULT_SUPPRESSION_CONFIG,
            found: false,
            errors: [],
        };
    }
    return loadConfigFromPath(configPath);
}
/**
 * Write suppression config to a file
 */
function writeSuppressionConfig(configPath, config) {
    const isYaml = configPath.endsWith('.yaml') || configPath.endsWith('.yml');
    let content;
    if (isYaml) {
        content = yaml.dump(config, {
            indent: 2,
            lineWidth: 100,
            noRefs: true,
            sortKeys: false,
        });
    }
    else {
        content = JSON.stringify(config, null, 2) + '\n';
    }
    (0, fs_1.writeFileSync)(configPath, content, 'utf-8');
}
/**
 * Add a finding suppression to the config file
 */
function addFindingSuppression(projectPath, suppression) {
    // Find or create config file
    let configPath = findConfigFile(projectPath);
    let config;
    if (configPath) {
        const result = loadConfigFromPath(configPath);
        if (result.errors.length > 0) {
            return {
                success: false,
                configPath: configPath,
                error: result.errors.join(', '),
            };
        }
        config = result.config;
    }
    else {
        // Create new config file in project root
        configPath = (0, path_1.join)(projectPath, '.oculum.yaml');
        config = { ...types_1.DEFAULT_SUPPRESSION_CONFIG };
    }
    // Initialize suppressions if needed
    if (!config.suppressions) {
        config.suppressions = {};
    }
    if (!config.suppressions.findings) {
        config.suppressions.findings = [];
    }
    // Check for duplicate
    const existingIndex = config.suppressions.findings.findIndex(f => f.hash === suppression.hash);
    if (existingIndex >= 0) {
        // Update existing suppression
        config.suppressions.findings[existingIndex] = suppression;
    }
    else {
        // Add new suppression
        config.suppressions.findings.push(suppression);
    }
    // Write config
    try {
        writeSuppressionConfig(configPath, config);
        return { success: true, configPath };
    }
    catch (err) {
        const message = err instanceof Error ? err.message : String(err);
        return { success: false, configPath, error: message };
    }
}
/**
 * Remove a finding suppression from the config file
 */
function removeFindingSuppression(projectPath, hash) {
    const configPath = findConfigFile(projectPath);
    if (!configPath) {
        return { success: true, removed: false };
    }
    const result = loadConfigFromPath(configPath);
    if (result.errors.length > 0) {
        return {
            success: false,
            configPath,
            error: result.errors.join(', '),
            removed: false,
        };
    }
    const config = result.config;
    if (!config.suppressions?.findings) {
        return { success: true, configPath, removed: false };
    }
    const initialLength = config.suppressions.findings.length;
    config.suppressions.findings = config.suppressions.findings.filter(f => f.hash !== hash);
    const removed = config.suppressions.findings.length < initialLength;
    if (removed) {
        try {
            writeSuppressionConfig(configPath, config);
        }
        catch (err) {
            const message = err instanceof Error ? err.message : String(err);
            return { success: false, configPath, error: message, removed: false };
        }
    }
    return { success: true, configPath, removed };
}
/**
 * Add a rule suppression to the config file
 */
function addRuleSuppression(projectPath, suppression) {
    // Find or create config file
    let configPath = findConfigFile(projectPath);
    let config;
    if (configPath) {
        const result = loadConfigFromPath(configPath);
        if (result.errors.length > 0) {
            return {
                success: false,
                configPath: configPath,
                error: result.errors.join(', '),
            };
        }
        config = result.config;
    }
    else {
        // Create new config file in project root
        configPath = (0, path_1.join)(projectPath, '.oculum.yaml');
        config = { ...types_1.DEFAULT_SUPPRESSION_CONFIG };
    }
    // Initialize suppressions if needed
    if (!config.suppressions) {
        config.suppressions = {};
    }
    if (!config.suppressions.rules) {
        config.suppressions.rules = [];
    }
    // Check for duplicate (same category and paths)
    const existingIndex = config.suppressions.rules.findIndex(r => r.category === suppression.category &&
        JSON.stringify(r.paths || []) === JSON.stringify(suppression.paths || []));
    if (existingIndex >= 0) {
        // Update existing suppression
        config.suppressions.rules[existingIndex] = suppression;
    }
    else {
        // Add new suppression
        config.suppressions.rules.push(suppression);
    }
    // Write config
    try {
        writeSuppressionConfig(configPath, config);
        return { success: true, configPath };
    }
    catch (err) {
        const message = err instanceof Error ? err.message : String(err);
        return { success: false, configPath, error: message };
    }
}
/**
 * List all suppressions in the config
 */
function listSuppressions(projectPath) {
    const result = loadSuppressionConfig(projectPath);
    return {
        rules: result.config.suppressions?.rules || [],
        findings: result.config.suppressions?.findings || [],
        configPath: result.configPath,
        errors: result.errors,
    };
}
/**
 * Check if a date string is expired
 */
function isExpired(dateString) {
    if (!dateString) {
        return false;
    }
    try {
        const expirationDate = new Date(dateString);
        const now = new Date();
        return expirationDate < now;
    }
    catch {
        // Invalid date format - treat as not expired
        return false;
    }
}
//# sourceMappingURL=config-loader.js.map