/**
 * failure-mode-summary card — deterministic projection over Report.summary.failureModes.
 *
 * Pure computation, no LLM call. Identifies the dominant failure mode
 * across all dimensions in the report's slim failure-mode summary.
 *
 * D-05: `.refine(buildFailureModeRefinement())` rejects cross-dimension
 * (dimension, failureMode) pairs that the schema otherwise would accept —
 * turning a "Zod-passes, semantically wrong" LLM output into a
 * `parseFailed: true` degraded card. For this deterministic card, the
 * refinement also defends against bad Report data.
 *
 * Schema is in the D0045 trust-boundary scan root; `satisfies` clause is
 * mandatory (cards/ is a SCAN_ROOT in check-trust-boundary-satisfies.ts).
 *
 * @see .planning/phases/05-diagnosis-engine-cli-llm-cards/05-CONTEXT.md (D-05)
 * @see packages/core/src/services/diagnosis/card-validators.ts
 */
import { z } from "zod";
import { CANONICAL_DIMENSIONS, failureModesForDimension, } from "../../../grader/failure-modes/index.js";
import { buildFailureModeRefinement } from "../card-validators.js";
// ---------------------------------------------------------------------------
// Body schema (D0045 trust boundary — satisfies required; D-05 refine)
// ---------------------------------------------------------------------------
export const FailureModeSummaryBodySchema = z
    .object({
    summary: z.string().min(1).max(800),
    dimension: z.string().min(1),
    failureMode: z.string().min(1),
    count: z.number().int().nonnegative(),
    sampleSize: z.number().int().nonnegative(),
})
    .refine(buildFailureModeRefinement(), {
    message: "failureMode is not in the canonical taxonomy for this dimension",
    path: ["failureMode"],
});
// ---------------------------------------------------------------------------
// Private helper — find the dimension a failure mode belongs to
// ---------------------------------------------------------------------------
/**
 * Per-family dimension preference order.
 *
 * Some failure modes (e.g., `missing-docs`) appear in multiple dimension
 * families. When we know the report's eval mode, we should resolve the mode
 * to a dimension in the matching family first, falling back to the linear
 * scan only when the mode-preferred family doesn't carry the failure mode.
 */
const MODE_TO_PREFERRED_DIMENSIONS = {
    literacy: ["task-completion", "code-correctness", "doc-coverage"],
    "mcp-server": [
        "mcp-behavior",
        "input-validation",
        "output-correctness",
        "error-handling",
        "security",
    ],
    "knowledge-probe": [
        "knowledge-probe",
        "factual-correctness",
        "completeness",
        "currency",
    ],
    "agent-harness": [
        "agent-harness",
        "process-quality",
        "agent-output",
        "tool-usage",
    ],
    custom: [],
};
/**
 * Find a canonical dimension whose taxonomy includes `mode`. When a
 * `preferredEvalMode` is supplied, prefer dimensions in the eval mode's
 * family — e.g. a mode appearing in both literacy and MCP resolves to MCP on
 * an MCP-only run. Falls back to the linear scan of CANONICAL_DIMENSIONS so
 * cross-cutting modes (and modes from unknown eval modes) still resolve.
 *
 * Returns `undefined` if the mode is not in any dimension's taxonomy.
 */
function findDimensionForMode(mode, preferredEvalMode) {
    if (preferredEvalMode) {
        const preferred = MODE_TO_PREFERRED_DIMENSIONS[preferredEvalMode] ?? [];
        for (const dim of preferred) {
            if (failureModesForDimension(dim).includes(mode)) {
                return dim;
            }
        }
    }
    for (const dim of CANONICAL_DIMENSIONS) {
        if (failureModesForDimension(dim).includes(mode)) {
            return dim;
        }
    }
    return undefined;
}
// ---------------------------------------------------------------------------
// Generator
// ---------------------------------------------------------------------------
export const generateFailureModeSummary = async (report) => {
    const slimFm = report.summary.failureModes;
    if (!slimFm ||
        !slimFm.topTitles ||
        slimFm.topTitles.length === 0 ||
        slimFm.totalJudgments === 0) {
        return {
            status: "missing",
            cardType: "failure-mode-summary",
            reason: "report has no failure modes",
        };
    }
    // Find the top entry — topTitles is already sorted by count descending
    const topEntry = slimFm.topTitles.reduce((best, entry) => (entry.count > best.count ? entry : best), slimFm.topTitles[0]);
    const failureMode = topEntry.category;
    const dimension = findDimensionForMode(failureMode, report.provenance?.mode);
    if (!dimension) {
        return {
            status: "missing",
            cardType: "failure-mode-summary",
            reason: `failure mode "${failureMode}" is not in the canonical taxonomy`,
        };
    }
    const sampleSize = slimFm.totalJudgments;
    const summary = `The most frequent failure mode is "${failureMode}" (${topEntry.count} of ${sampleSize} judgments in dimension "${dimension}").`;
    const body = FailureModeSummaryBodySchema.parse({
        summary,
        dimension,
        failureMode,
        count: topEntry.count,
        sampleSize,
    });
    return {
        status: "ready",
        cardType: "failure-mode-summary",
        body,
        meta: {
            cardVersion: "failure-mode-summary@0.1.0",
            generatedAt: new Date().toISOString(),
        },
    };
};
