import { hashProjectPath } from '../storage/base-store.js';
import { specStore } from '../storage/index.js';
import { saveActual, listActuals, saveCalibration } from '../storage/actuals-store.js';
import { computeDeviationPct, generateAccuracyReport, computeCalibrationFactors, } from '../engine/estimation-calibrator.js';
import { computeCalibrationTrend } from '../engine/estimation-predictor.js';
// ---------------------------------------------------------------------------
// Internal helpers
// ---------------------------------------------------------------------------
function resolveProjectId(params) {
    if (params.projectId) {
        return params.projectId;
    }
    if (params.projectPath) {
        return hashProjectPath(params.projectPath);
    }
    return null;
}
// ---------------------------------------------------------------------------
// record_actual handler
// ---------------------------------------------------------------------------
export async function handleRecordActual(input) {
    const { specId, actualHours, notes } = input;
    const projectId = resolveProjectId(input);
    if (!projectId) {
        return {
            content: [{ type: 'text', text: 'Provide projectId or projectPath.' }],
            isError: true,
        };
    }
    if (actualHours < 0) {
        return {
            content: [{ type: 'text', text: 'actualHours must be a non-negative number.' }],
            isError: true,
        };
    }
    const spec = await specStore.getSpec(projectId, specId);
    if (!spec) {
        return {
            content: [{ type: 'text', text: `Spec '${specId}' not found.` }],
            isError: true,
        };
    }
    const estimatedHours = spec.estimation.devHours + spec.estimation.reviewHours;
    const deviationPct = computeDeviationPct(estimatedHours, actualHours);
    const specType = spec.type;
    const specScope = spec.scope;
    const record = {
        specId,
        estimatedHours,
        actualHours,
        deviationPct,
        specType,
        specScope,
        recordedAt: new Date().toISOString(),
        ...(notes !== undefined ? { notes } : {}),
    };
    await saveActual(projectId, record);
    const deviationLabel = deviationPct > 0 ? `+${deviationPct.toFixed(1)}%` : `${deviationPct.toFixed(1)}%`;
    return {
        content: [
            {
                type: 'text',
                text: [
                    `Actual recorded for ${specId}.`,
                    `Estimated: ${String(estimatedHours)}h | Actual: ${String(actualHours)}h | Deviation: ${deviationLabel}`,
                    notes ? `Notes: ${notes}` : '',
                ]
                    .filter(Boolean)
                    .join('\n'),
            },
        ],
        structuredContent: {
            specId,
            estimatedHours,
            actualHours,
            deviationPct,
            recordedAt: record.recordedAt,
        },
    };
}
// ---------------------------------------------------------------------------
// estimation_accuracy_report handler
// ---------------------------------------------------------------------------
export async function handleEstimationAccuracyReport(input) {
    const projectId = resolveProjectId(input);
    if (!projectId) {
        return {
            content: [{ type: 'text', text: 'Provide projectId or projectPath.' }],
            isError: true,
        };
    }
    let records = await listActuals(projectId);
    if (input.scope !== undefined) {
        records = records.filter((r) => r.specScope === input.scope);
    }
    if (input.type !== undefined) {
        records = records.filter((r) => r.specType === input.type);
    }
    if (input.limit !== undefined && input.limit > 0) {
        records = records.slice(-input.limit);
    }
    if (records.length === 0) {
        return {
            content: [
                {
                    type: 'text',
                    text: 'No actuals recorded yet. Use record_actual after completing specs.',
                },
            ],
        };
    }
    const report = generateAccuracyReport(projectId, records);
    // SPEC-570: Calibration Trend section
    const trend = computeCalibrationTrend(records);
    const trendLines = [];
    if (trend.length >= 2) {
        trendLines.push('', 'Calibration Trend (bias ratio — 1.0 = perfect accuracy):');
        for (const qt of trend) {
            const direction = qt.avgBias > 1.05
                ? ' (under-estimating)'
                : qt.avgBias < 0.95
                    ? ' (over-estimating)'
                    : ' (accurate)';
            trendLines.push(`  ${qt.quarter}: bias=${String(qt.avgBias)} (${String(qt.sampleSize)} specs)${direction}`);
        }
        const first = trend.at(0);
        const last = trend.at(-1);
        if (first !== undefined && last !== undefined) {
            const improving = Math.abs(last.avgBias - 1) < Math.abs(first.avgBias - 1);
            trendLines.push(improving ? '  Trend: improving' : '  Trend: not improving yet');
        }
    }
    const lines = [
        `Estimation Accuracy Report — ${String(report.totalRecords)} specs`,
        `Avg deviation: ${String(report.aggregate.avgDeviationPct)}% | Median: ${String(report.aggregate.medianDeviationPct)}%`,
        `Outliers (>50% deviation): ${String(report.aggregate.outlierCount)}`,
        '',
        'By scope:',
        ...report.byScope.map((s) => `  ${s.scope}: avg ${String(s.avgDeviationPct)}% (${String(s.count)} specs, multiplier: ${String(s.suggestedMultiplier)}x)`),
        '',
        'By type:',
        ...report.byType.map((t) => `  ${t.type}: avg ${String(t.avgDeviationPct)}% (${String(t.count)} specs)`),
        ...trendLines,
    ];
    return {
        content: [{ type: 'text', text: lines.join('\n') }],
        structuredContent: {
            ...report,
            calibrationTrend: trend,
        },
    };
}
// ---------------------------------------------------------------------------
// calibrate_estimates handler
// ---------------------------------------------------------------------------
export async function handleCalibrateEstimates(input) {
    const projectId = resolveProjectId(input);
    if (!projectId) {
        return {
            content: [{ type: 'text', text: 'Provide projectId or projectPath.' }],
            isError: true,
        };
    }
    const minSamples = input.minSamples ?? 3;
    const records = await listActuals(projectId);
    if (records.length === 0) {
        return {
            content: [
                {
                    type: 'text',
                    text: 'No actuals recorded yet. Cannot calibrate without historical data.',
                },
            ],
        };
    }
    const factors = computeCalibrationFactors(projectId, records, minSamples);
    await saveCalibration(projectId, factors);
    const scopeEntries = Object.entries(factors.scopeMultipliers);
    const typeEntries = Object.entries(factors.typeMultipliers);
    const lines = [
        `Calibration updated — based on ${String(records.length)} actuals (min samples: ${String(minSamples)}).`,
        '',
        scopeEntries.length > 0
            ? `Scope multipliers updated:\n${scopeEntries.map(([k, v]) => `  ${k}: ${String(v)}x`).join('\n')}`
            : 'No scope multipliers updated (insufficient samples or deviation <20%).',
        '',
        typeEntries.length > 0
            ? `Type multipliers updated:\n${typeEntries.map(([k, v]) => `  ${k}: ${String(v)}x`).join('\n')}`
            : 'No type multipliers updated (insufficient samples or deviation <20%).',
    ];
    return {
        content: [{ type: 'text', text: lines.filter(Boolean).join('\n') }],
        structuredContent: factors,
    };
}
//# sourceMappingURL=estimation-accuracy-handler.js.map