"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PERFORMANCE_BENCHMARK_SCHEMA_VERSION = void 0;
exports.runPerformanceBenchmark = runPerformanceBenchmark;
exports.evaluatePerformanceRegressions = evaluatePerformanceRegressions;
exports.PERFORMANCE_BENCHMARK_SCHEMA_VERSION = 1;
const METRICS = ["scanCount", "outputBytes", "peakMemoryBytes", "elapsedMs"];
async function runPerformanceBenchmark(input) {
    const workflowId = normalizeWorkflowId(input.workflowId);
    if (!Number.isSafeInteger(input.warmIterations) || input.warmIterations < 1 || input.warmIterations > 100) {
        throw new Error("Warm iterations must be a safe integer from 1 through 100.");
    }
    if (typeof input.run !== "function")
        throw new Error("Performance benchmark runner must be a function.");
    const cold = await sample(input.run, "cold", 0);
    const warm = [];
    for (let iteration = 0; iteration < input.warmIterations; iteration += 1) {
        warm.push(await sample(input.run, "warm", iteration));
    }
    return {
        schemaVersion: exports.PERFORMANCE_BENCHMARK_SCHEMA_VERSION,
        workflowId,
        cold,
        warm,
        coldMaximums: aggregate([cold]),
        warmMaximums: aggregate(warm)
    };
}
function evaluatePerformanceRegressions(reports, baselines) {
    const reportMap = uniqueMap(reports, (report) => validateReport(report).workflowId, "report");
    const baselineMap = uniqueMap(baselines, (baseline) => validateBaseline(baseline).workflowId, "baseline");
    if (reportMap.size !== baselineMap.size || [...reportMap.keys()].some((key) => !baselineMap.has(key))) {
        throw new Error("Performance reports and baselines must contain the same workflow ids.");
    }
    const findings = [];
    for (const workflowId of [...reportMap.keys()].sort()) {
        const report = reportMap.get(workflowId);
        const baseline = baselineMap.get(workflowId);
        for (const metric of METRICS) {
            const severity = metric === "elapsedMs" ? "advisory" : "blocking";
            addFinding(findings, workflowId, metric, "cold-maximum", severity, report.coldMaximums[metric], baseline[metric].coldMaximum);
            addFinding(findings, workflowId, metric, "warm-maximum", severity, report.warmMaximums[metric], baseline[metric].warmMaximum);
            const ratio = report.coldMaximums[metric] === 0
                ? (report.warmMaximums[metric] === 0 ? 0 : Number.POSITIVE_INFINITY)
                : report.warmMaximums[metric] / report.coldMaximums[metric];
            addFinding(findings, workflowId, metric, "warm-to-cold-ratio", severity, ratio, baseline[metric].warmToColdRatioMaximum);
        }
    }
    const blockingFindings = findings.filter((finding) => finding.severity === "blocking");
    return { passed: blockingFindings.length === 0, blockingFindings, advisoryFindings: findings.filter((finding) => finding.severity === "advisory") };
}
async function sample(run, kind, iteration) {
    const result = await run(Object.freeze({ kind, iteration }));
    return { kind, iteration, ...normalizeAggregate(result) };
}
function aggregate(samples) {
    const maximums = { scanCount: 0, outputBytes: 0, peakMemoryBytes: 0, elapsedMs: 0 };
    for (const sample of samples)
        for (const metric of METRICS)
            maximums[metric] = Math.max(maximums[metric], sample[metric]);
    return maximums;
}
function normalizeAggregate(value) {
    if (value === null || typeof value !== "object")
        throw new Error("Performance sample must be an object.");
    return Object.fromEntries(METRICS.map((metric) => [metric, metricValue(value[metric], `Performance sample ${metric}`)]));
}
function validateReport(report) {
    if (report.schemaVersion !== 1 || report.warm.length < 1)
        throw new Error("Performance benchmark report is invalid or unsupported.");
    normalizeWorkflowId(report.workflowId);
    normalizeAggregate(report.coldMaximums);
    normalizeAggregate(report.warmMaximums);
    return report;
}
function validateBaseline(baseline) {
    normalizeWorkflowId(baseline.workflowId);
    for (const metric of METRICS) {
        const threshold = baseline[metric];
        metricValue(threshold?.coldMaximum, `${metric} cold maximum`);
        metricValue(threshold?.warmMaximum, `${metric} warm maximum`);
        const ratio = threshold?.warmToColdRatioMaximum;
        if (typeof ratio !== "number" || !Number.isFinite(ratio) || ratio < 0)
            throw new Error(`${metric} ratio maximum must be a non-negative finite number.`);
    }
    return baseline;
}
function uniqueMap(values, key, label) {
    if (values.length === 0)
        throw new Error(`At least one performance ${label} is required.`);
    const map = new Map();
    for (const value of values) {
        const id = key(value);
        if (map.has(id))
            throw new Error(`Performance ${label} workflow ids must be unique.`);
        map.set(id, value);
    }
    return map;
}
function addFinding(findings, workflowId, metric, comparison, severity, actual, threshold) {
    if (actual > threshold)
        findings.push({ workflowId, metric, comparison, severity, actual, threshold });
}
function normalizeWorkflowId(value) {
    const normalized = value?.trim().toLowerCase();
    if (!/^[a-z0-9][a-z0-9._-]{0,63}$/.test(normalized))
        throw new Error("Performance workflow id must be a bounded opaque identifier.");
    return normalized;
}
function metricValue(value, label) {
    if (!Number.isSafeInteger(value) || value < 0)
        throw new Error(`${label} must be a non-negative safe integer.`);
    return value;
}
