/**
 * `symbol_findings`, one row per symbol-preflight finding (D0091).
 *
 * The deterministic-grader signal: for each API binding a candidate imported,
 * did it resolve against the real package (`exists`), was it hallucinated
 * (`missing`), or could the resolver not decide (`unresolved`)? Answers
 * "which APIs get hallucinated most", the framework's headline measurement.
 *
 * Grain is one `SymbolPreflightFinding` inside one `symbolPreflight` entry
 * (per run × mode × task × model). A run without the deterministic lane yields
 * zero rows.
 *
 * Derives from the canonical `SymbolPreflightReport` core type (D0045).
 *
 * @see packages/core/src/types/symbol-preflight-report.ts
 * @see docs/design-docs/bigquery-analytics-fact-loader.md
 */
import { z } from "zod";
import { defineFactTable, } from "../descriptor.js";
import { axisString, LOADED_AT_COLUMN, numOrNull, runFactBase, runFactBaseShape, RUN_FACT_BASE_COLUMNS, strOrNull, } from "./common.js";
const rowSchema = z.object({
    ...runFactBaseShape,
    mode: z.string(),
    task: z.string(),
    model_id: z.string(),
    pkg: z.string(),
    binding: z.string(),
    symbol: z.string(),
    status: z.string(),
    symbol_source: z.string().nullable(),
    reason: z.string().nullable(),
    deduction: z.number(),
});
const schema = [
    ...RUN_FACT_BASE_COLUMNS,
    { name: "mode", type: "STRING" },
    { name: "task", type: "STRING" },
    { name: "model_id", type: "STRING" },
    { name: "pkg", type: "STRING", description: "Package resolved against." },
    { name: "binding", type: "STRING", description: "Imported binding name." },
    {
        name: "symbol",
        type: "STRING",
        description: "Stable identity pkg.binding.",
    },
    {
        name: "status",
        type: "STRING",
        description: "exists | missing | unresolved.",
    },
    {
        name: "symbol_source",
        type: "STRING",
        description: "types | runtime (exists only).",
    },
    { name: "reason", type: "STRING", description: "Unresolved reason." },
    {
        name: "deduction",
        type: "FLOAT64",
        mode: "REQUIRED",
        description: "Nominal per-finding deduction.",
    },
    LOADED_AT_COLUMN,
];
// ---------------------------------------------------------------------------
// Extractor
// ---------------------------------------------------------------------------
function extract(ctx) {
    const rows = [];
    for (const entry of ctx.artifacts.entries("symbolPreflight")) {
        const report = entry.payload;
        const findings = Array.isArray(report.findings) ? report.findings : [];
        const perMissing = numOrNull(report.deduction?.perMissing) ?? 0;
        const base = runFactBase(ctx, entry.sourceUri);
        const mode = axisString(entry.association.mode);
        const task = axisString(entry.association.task);
        const modelId = axisString(entry.association.model);
        for (const raw of findings) {
            const status = strOrNull(raw.result) ?? "unknown";
            const pkg = strOrNull(raw.pkg) ?? "unknown";
            const binding = strOrNull(raw.binding) ?? "unknown";
            rows.push({
                ...base,
                mode,
                task,
                model_id: modelId,
                pkg,
                binding,
                symbol: `${pkg}.${binding}`,
                status,
                symbol_source: status === "exists" ? strOrNull(raw.source) : null,
                reason: status === "unresolved" ? strOrNull(raw.reason) : null,
                deduction: status === "missing" ? perMissing : 0,
            });
        }
    }
    return rows;
}
export const symbolFindings = defineFactTable({
    table: "symbol_findings",
    dataset: "ailf_facts",
    sourceArtifacts: ["symbolPreflight"],
    schema,
    rowSchema,
    partitionField: "completed_at",
    clusterFields: ["mode", "model_id", "status"],
    mergeKey: ["run_id", "mode", "task", "model_id", "symbol"],
    extract,
});
export { rowSchema as symbolFindingFactRowSchema };
