/**
 * regression-vs-baseline card — LLM-driven run comparison card.
 *
 * Model: claude-opus-4-6 (high-stakes per AI-SPEC §4 model routing)
 * Version: regression-vs-baseline@0.1.0
 *
 * DIAG-05: emits ONLY when `ctx.baseline` is set. When baseline is absent,
 * returns `status: "missing", reason: "no --compare baseline supplied"`.
 *
 * Mitigations:
 * - failure-mode #1: `buildRegressionVsBaselinePrompt` computes deltas in JS
 *   BEFORE the LLM call; schema refine asserts sign-consistency (R3)
 *
 * Schema is in the D0045 trust-boundary scan root; `satisfies` clause is
 * mandatory.
 *
 * @see .planning/phases/05-diagnosis-engine-cli-llm-cards/05-AI-SPEC.md §3 lines 603-664
 */
import { z } from "zod";
import { modelId as mkModelId } from "../../../ports/llm-client.js";
import { buildRegressionVsBaselinePrompt } from "../prompt-builders.js";
// ---------------------------------------------------------------------------
// Helper: direction sign check
// ---------------------------------------------------------------------------
/**
 * Map direction label to its expected sign. Returns the expected sign:
 * +1 for improved, -1 for regressed, 0 for unchanged.
 */
function directionSign(direction) {
    if (direction === "improved")
        return 1;
    if (direction === "regressed")
        return -1;
    return 0;
}
// ---------------------------------------------------------------------------
// Body schema (D0045 trust boundary — satisfies required)
// ---------------------------------------------------------------------------
/**
 * Module-level static shape only. Per-call adds:
 * - sign-consistency refine (R3): Math.sign(pointsDelta) === directionSign(direction)
 */
export const RegressionVsBaselineBodySchema = z.object({
    summary: z.string().min(1).max(800),
    deltas: z
        .array(z.object({
        area: z.string().min(1),
        direction: z.enum(["improved", "regressed", "unchanged"]),
        pointsDelta: z.number(),
        drivers: z.array(z.string()),
    }))
        .max(10),
    overallTrend: z.enum(["net-improved", "net-regressed", "mixed", "stable"]),
});
// ---------------------------------------------------------------------------
// Generator
// ---------------------------------------------------------------------------
const CARD_MODEL = mkModelId("anthropic:claude-opus-4-6");
export const generateRegressionVsBaseline = async (report, ctx) => {
    // C1: no LLM → missing
    if (!ctx.llm) {
        return {
            status: "missing",
            cardType: "regression-vs-baseline",
            reason: "no LLMClient wired",
        };
    }
    // R1: DIAG-05 no-auto-comparison — only emits when baseline is supplied
    if (!ctx.baseline) {
        return {
            status: "missing",
            cardType: "regression-vs-baseline",
            reason: "no --compare baseline supplied",
        };
    }
    // Compute deltas in JS BEFORE the LLM call (failure-mode #1 mitigation).
    // The prompt embeds the precomputed numbers so the LLM cannot fabricate deltas.
    const { system, user } = buildRegressionVsBaselinePrompt(report, ctx.baseline);
    // Per-call schema: additive R3 sign-consistency refine
    // (AI-SPEC §3 Pitfall 1 — schema local to generator, not module scope)
    const PerCallSchema = z
        .object({
        summary: z.string().min(1).max(800),
        deltas: z
            .array(z.object({
            area: z.string().min(1),
            direction: z.enum(["improved", "regressed", "unchanged"]),
            pointsDelta: z.number(),
            drivers: z.array(z.string()),
        }))
            .max(10),
        overallTrend: z.enum([
            "net-improved",
            "net-regressed",
            "mixed",
            "stable",
        ]),
    })
        // R3: sign-consistency guardrail — LLM must not invert direction vs delta
        .refine((body) => body.deltas.every((d) => Math.sign(d.pointsDelta) === directionSign(d.direction)), {
        message: "direction label must match sign of pointsDelta (improved=positive, regressed=negative, unchanged=zero)",
        path: ["deltas"],
    });
    // Destructure `cost` and `model` from the LLMClient return —
    // already provided per llm-client.ts:139-144, previously discarded.
    const { value, usage, cost, model } = await ctx.llm.completeStructured({
        model: CARD_MODEL,
        prompt: `${system}\n\n${user}`,
        schema: PerCallSchema,
        temperature: 0.1,
        maxTokens: 2000,
        context: {
            feature: "diagnosis",
            runId: ctx.runId,
            cardId: "regression-vs-baseline",
        },
    });
    return {
        status: "ready",
        cardType: "regression-vs-baseline",
        body: value,
        meta: {
            cardVersion: "regression-vs-baseline@0.1.0",
            tokenUsage: { input: usage.promptTokens, output: usage.completionTokens },
            generatedAt: new Date().toISOString(),
            cost,
            model,
        },
    };
};
