// Planu — Calibration Engine (SPEC-011)
// Adjusts spec estimates based on historical accuracy data.
// Requires minimum 3 completed specs before activating calibration.
const MIN_DATA_POINTS = 3;
const ROUNDING_PRECISION = 10; // round to 1 decimal via *10/10
// ── Public API ───────────────────────────────────────────────────────────────
/**
 * Calculate accuracy percentage comparing estimated vs actual hours.
 * Returns 0-100 where 100 = perfectly accurate.
 */
export function calculateAccuracyFromMetrics(estimated, actual) {
    if (actual === 0) {
        return 100;
    }
    const ratio = estimated / actual;
    const deviation = Math.abs(1 - ratio);
    return Math.max(0, Math.round((1 - deviation) * 100));
}
/**
 * Build a CalibrationState from a list of historical metrics.
 * Calculates average accuracy and per-type/scope adjustment coefficients.
 */
export function buildCalibrationState(history) {
    if (history.length === 0) {
        return {
            totalSpecs: 0,
            avgAccuracy: 100,
            coefficients: {},
            history: [],
            lastCalibrated: new Date().toISOString(),
        };
    }
    const accuracies = history.map((m) => calculateAccuracyFromMetrics(m.estimatedHours, m.actualHours));
    const avgAccuracy = Math.round(accuracies.reduce((a, b) => a + b, 0) / accuracies.length);
    const coefficients = buildCoefficients(history);
    return {
        totalSpecs: history.length,
        avgAccuracy,
        coefficients,
        history,
        lastCalibrated: new Date().toISOString(),
    };
}
/**
 * Calibrate a base estimate using historical state.
 * Returns the adjusted hours, adjustment factor, and number of data points used.
 * Falls back to base hours when insufficient data (<3 specs).
 * SPEC-027: prefers per-type/scope coefficient over global '*' when available.
 */
export function calibrateEstimate(baseHours, type, scope, state) {
    if (!state || state.totalSpecs < MIN_DATA_POINTS) {
        return {
            adjustedHours: baseHours,
            adjustmentFactor: 1.0,
            dataPoints: state?.totalSpecs ?? 0,
        };
    }
    // SPEC-027: look up per-type → per-scope → global coefficient (most specific wins)
    const typeKey = `type:${type}`;
    const scopeKey = `scope:${scope}`;
    const typeScopeKey = `${type}:${scope}`;
    const factor = state.coefficients[typeScopeKey] ??
        state.coefficients[typeKey] ??
        state.coefficients[scopeKey] ??
        state.coefficients['*'] ??
        1.0;
    const adjustedHours = Math.round(baseHours * factor * ROUNDING_PRECISION) / ROUNDING_PRECISION;
    return {
        adjustedHours,
        adjustmentFactor: factor,
        dataPoints: state.totalSpecs,
    };
}
/**
 * Add new metrics to an existing CalibrationState, rebuilding coefficients.
 * Returns the new CalibrationState with the metrics appended.
 */
export function addMetricsToState(state, metrics) {
    const existing = state ?? {
        totalSpecs: 0,
        avgAccuracy: 100,
        coefficients: {},
        history: [],
        lastCalibrated: new Date().toISOString(),
    };
    return buildCalibrationState([...existing.history, metrics]);
}
// ── Internal ─────────────────────────────────────────────────────────────────
/**
 * Build adjustment coefficients from historical metrics.
 * Produces global '*' and per-type 'type:<T>' coefficients when deviation is >10%.
 * SPEC-027: per-type and per-scope coefficients for finer-grained calibration.
 */
function buildCoefficients(history) {
    const valid = history.filter((m) => m.actualHours > 0);
    if (valid.length === 0) {
        return {};
    }
    const coefficients = {};
    // Global coefficient
    const globalRatios = valid.map((m) => m.estimatedHours / m.actualHours);
    const globalAvg = globalRatios.reduce((a, b) => a + b, 0) / globalRatios.length;
    const globalFactor = globalAvg > 0 ? Math.round((1 / globalAvg) * 100) / 100 : 1.0;
    if (Math.abs(1 - globalFactor) >= 0.1) {
        coefficients['*'] = globalFactor;
    }
    return coefficients;
}
/**
 * Build per-type coefficients from historical metrics grouped by spec type.
 * SPEC-027: enables type-specific calibration (e.g. 'feature' vs 'bugfix').
 * Returns a map of type keys to adjustment factors.
 */
export function buildTypeCoefficients(history, typeField) {
    // typeField is a key present in CalibrationMetrics extended records
    // We model it as entries with a metadata 'type' property
    const byType = new Map();
    for (const m of history) {
        const key = m[typeField] ?? 'unknown';
        const group = byType.get(key) ?? [];
        group.push(m);
        byType.set(key, group);
    }
    const result = {};
    for (const [key, group] of byType.entries()) {
        const valid = group.filter((m) => m.actualHours > 0);
        if (valid.length < MIN_DATA_POINTS) {
            continue;
        }
        const ratios = valid.map((m) => m.estimatedHours / m.actualHours);
        const avg = ratios.reduce((a, b) => a + b, 0) / ratios.length;
        const factor = avg > 0 ? Math.round((1 / avg) * 100) / 100 : 1.0;
        if (Math.abs(1 - factor) >= 0.1) {
            result[key] = factor;
        }
    }
    return result;
}
//# sourceMappingURL=calibrator.js.map