import { readFileSync, existsSync, renameSync, readdirSync, unlinkSync } from "fs";
import { join } from "path";
import { secureMkdirSync, secureWriteFileSync } from "../../utils/secureWrite.js";
import { KASTELL_DIR } from "../../utils/paths.js";
import { withFileLock } from "../../utils/fileLock.js";
import { formatRelativeTime } from "../../utils/dates.js";
const REGRESSION_DIR = join(KASTELL_DIR, "regression");
export function getBaselinePath(serverIp) {
    const safeIp = serverIp.replace(/\./g, "-");
    return join(REGRESSION_DIR, `${safeIp}.json`);
}
export function loadBaseline(serverIp) {
    const filePath = getBaselinePath(serverIp);
    if (!existsSync(filePath))
        return null;
    try {
        const raw = readFileSync(filePath, "utf-8");
        const parsed = JSON.parse(raw);
        if (parsed.version !== 1 || !Array.isArray(parsed.passedChecks))
            return null;
        return parsed;
    }
    catch {
        return null;
    }
}
export function extractPassedCheckIds(audit) {
    const ids = [];
    for (const category of audit.categories) {
        for (const check of category.checks) {
            if (check.passed)
                ids.push(check.id);
        }
    }
    return ids.sort();
}
export async function saveBaseline(audit, existing, passedCheckIds) {
    const filePath = getBaselinePath(audit.serverIp);
    await withFileLock(filePath, () => {
        const prev = existing ?? loadBaseline(audit.serverIp);
        const passedChecks = passedCheckIds ?? extractPassedCheckIds(audit);
        const bestScore = prev
            ? Math.max(prev.bestScore, audit.overallScore)
            : audit.overallScore;
        const baseline = {
            version: 1,
            serverIp: audit.serverIp,
            lastUpdated: new Date().toISOString(),
            bestScore,
            passedChecks,
        };
        secureMkdirSync(REGRESSION_DIR, { recursive: true });
        const tmpFile = filePath + ".tmp";
        secureWriteFileSync(tmpFile, JSON.stringify(baseline, null, 2), { encoding: "utf-8" });
        renameSync(tmpFile, filePath);
    });
}
export async function saveBaselineSafe(audit, existing, passedCheckIds) {
    await saveBaseline(audit, existing, passedCheckIds).catch(() => { });
}
export function checkRegression(baseline, audit, passedCheckIds) {
    const currentPassed = new Set(passedCheckIds ?? extractPassedCheckIds(audit));
    const baselinePassed = new Set(baseline.passedChecks);
    const regressions = [];
    for (const id of baselinePassed) {
        if (!currentPassed.has(id))
            regressions.push(id);
    }
    const newPasses = [];
    for (const id of currentPassed) {
        if (!baselinePassed.has(id))
            newPasses.push(id);
    }
    return {
        regressions: regressions.sort(),
        newPasses: newPasses.sort(),
        baselineScore: baseline.bestScore,
        currentScore: audit.overallScore,
    };
}
export function hasRegression(result) {
    return result.regressions.length > 0 || result.currentScore < result.baselineScore;
}
export function shouldUpdateBaseline(regression, forced) {
    if (!regression)
        return true;
    if (forced)
        return true;
    return !hasRegression(regression);
}
export function formatRegressionSummary(result) {
    const lines = [];
    if (result.regressions.length > 0) {
        lines.push({
            severity: "warning",
            text: `Regression: ${result.regressions.length} check(s) regressed: ${result.regressions.join(", ")}`,
        });
    }
    if (result.newPasses.length > 0) {
        lines.push({
            severity: "info",
            text: `New passes: ${result.newPasses.length} check(s) now passing: ${result.newPasses.join(", ")}`,
        });
    }
    lines.push({ severity: "info", text: `Best score: ${result.baselineScore}` });
    return lines;
}
export function listBaselines() {
    if (!existsSync(REGRESSION_DIR))
        return [];
    const files = readdirSync(REGRESSION_DIR, { withFileTypes: true })
        .filter((f) => f.isFile() && f.name.endsWith(".json"));
    const baselines = [];
    for (const file of files) {
        try {
            const raw = readFileSync(join(REGRESSION_DIR, file.name), "utf-8");
            const parsed = JSON.parse(raw);
            if (parsed.version === 1 && Array.isArray(parsed.passedChecks))
                baselines.push(parsed);
        }
        catch {
            // skip corrupt files
        }
    }
    return baselines;
}
export { formatRelativeTime } from "../../utils/dates.js";
export function formatBaselineStatus(baseline) {
    const lastUpdated = formatRelativeTime(baseline.lastUpdated);
    return [
        `Server: ${baseline.serverIp}`,
        `Best Score: ${baseline.bestScore}`,
        `Tracked Checks: ${baseline.passedChecks.length}`,
        `Last Updated: ${lastUpdated}`,
    ].join("\n");
}
export function deleteBaseline(serverIp) {
    const filePath = getBaselinePath(serverIp);
    try {
        unlinkSync(filePath);
    }
    catch (err) {
        if (err.code === "ENOENT") {
            throw new Error(`No baseline found for ${serverIp}`, { cause: err });
        }
        throw err;
    }
}
//# sourceMappingURL=regression.js.map