import { redact } from "@poe-code/acp-telemetry";
function defineRecordEntry(record, key, value) {
    Object.defineProperty(record, key, {
        configurable: true,
        enumerable: true,
        value,
        writable: true,
    });
}
export function makePipelineRowState(client) {
    const rows = new Map();
    const completedRows = new Set();
    return {
        start(progress) {
            try {
                const key = pipelineKey(progress);
                const previousRow = rows.get(key);
                if (previousRow?.span) {
                    void previousRow.span.then((span) => span?.end()).catch((err) => {
                        client.recordError(err, "pipeline step start");
                    });
                }
                completedRows.delete(key);
                const row = {
                    progress,
                    span: openCurrentChildSpan(client, {
                        name: `step:${readPipelineStep(progress)}:${readPipelineIndex(progress)}`,
                        type: "task",
                    }, "pipeline step start"),
                };
                rows.set(key, row);
            }
            catch (err) {
                client.recordError(err, "pipeline step start");
            }
        },
        complete(progress) {
            try {
                const key = pipelineKey(progress);
                if (completedRows.has(key)) {
                    return;
                }
                completedRows.add(key);
                const row = rows.get(key);
                rows.delete(key);
                void (async () => {
                    try {
                        const existingSpan = row?.span === undefined ? undefined : await row.span;
                        const span = existingSpan ?? await openCurrentChildSpan(client, {
                            name: `step:${readPipelineStep(progress)}:${readPipelineIndex(progress)}`,
                            type: "task",
                        }, "pipeline step complete");
                        if (span === undefined) {
                            return;
                        }
                        try {
                            span.log(buildPipelineCompletionLog(row?.progress ?? progress, progress));
                        }
                        finally {
                            span.end();
                        }
                    }
                    catch (err) {
                        client.recordError(err, "pipeline step complete");
                    }
                })();
            }
            catch (err) {
                client.recordError(err, "pipeline step complete");
            }
        },
    };
}
export async function logSuperintendentRole(client, role, result) {
    try {
        const span = await openCurrentChildSpan(client, {
            name: `role:${role}`,
            type: "task",
        }, `superintendent ${role}`);
        if (span === undefined) {
            return;
        }
        try {
            span.log(buildSuperintendentLog(role, result));
        }
        finally {
            span.end();
        }
    }
    catch (err) {
        client.recordError(err, `superintendent ${role}`);
    }
}
export function makeExperimentIterationState(client, experimentName) {
    const rows = new Map();
    let latestBaseline;
    return {
        async start(index, agent) {
            const previous = rows.get(index);
            if (previous !== undefined) {
                const previousSpan = await previous.span;
                previousSpan?.end();
            }
            const row = {
                span: (async () => {
                    try {
                        const experiment = asSpanParent(await client.getExperiment(experimentName));
                        return experiment.startSpan({
                            name: `iteration:${index}`,
                            type: "task",
                        });
                    }
                    catch (err) {
                        client.recordError(err, `experiment ${experimentName} iteration start`);
                        return undefined;
                    }
                })(),
                agent,
                iteration: index,
                baseline: latestBaseline,
                metrics: {},
            };
            rows.set(index, row);
            await row.span;
        },
        baseline(b) {
            try {
                latestBaseline = finiteNumberRecord(b);
                for (const row of rows.values()) {
                    row.baseline = latestBaseline;
                }
            }
            catch (err) {
                client.recordError(err, `experiment ${experimentName} baseline`);
            }
        },
        metric(name, value) {
            try {
                const row = readLatestRow(rows);
                if (row !== undefined && Number.isFinite(value)) {
                    defineRecordEntry(row.metrics, name, value);
                }
            }
            catch (err) {
                client.recordError(err, `experiment ${experimentName} metric`);
            }
        },
        commit(hash) {
            try {
                const row = readLatestRow(rows);
                if (row !== undefined) {
                    row.commit = hash;
                }
            }
            catch (err) {
                client.recordError(err, `experiment ${experimentName} commit`);
            }
        },
        reset(hash) {
            try {
                const row = readLatestRow(rows);
                if (row !== undefined) {
                    row.reset = hash;
                }
            }
            catch (err) {
                client.recordError(err, `experiment ${experimentName} reset`);
            }
        },
        async complete(index, entry) {
            const row = rows.get(index);
            rows.delete(index);
            try {
                if (row === undefined) {
                    return;
                }
                const span = await row.span;
                if (span === undefined)
                    return;
                try {
                    span.log(buildExperimentLog(row, entry));
                }
                finally {
                    span.end();
                }
            }
            catch (err) {
                client.recordError(err, `experiment ${experimentName} iteration complete`);
            }
        },
    };
}
async function openCurrentChildSpan(client, args, ctx) {
    try {
        const { currentSpan } = await import("braintrust");
        return asSpanParent(currentSpan()).startSpan(args);
    }
    catch (err) {
        client.recordError(err, ctx);
        return undefined;
    }
}
function buildPipelineCompletionLog(started, completed) {
    const startRecord = asRecord(started) ?? {};
    const completeRecord = asRecord(completed) ?? {};
    return {
        input: redact({
            step_name: readPipelineStep(started),
            step_prompt: readFirstString(startRecord, ["step_prompt", "stepPrompt", "prompt"]),
            plan_section: readFirstString(startRecord, ["plan_section", "planSection", "section"]),
        }),
        output: redact({
            result_summary: readFirstString(completeRecord, [
                "result_summary",
                "resultSummary",
                "summary",
            ]),
            files_changed: readFirstValue(completeRecord, ["files_changed", "filesChanged"]),
            success: completed.success,
        }),
        scores: {
            passed: completed.success ? 1 : 0,
        },
        metrics: buildPipelineMetrics(completed),
    };
}
export function buildSuperintendentLog(role, result) {
    const record = asRecord(result);
    const event = {
        input: redact(record?.input),
        output: redact(record?.output ?? result),
    };
    if (role === "inspector") {
        const satisfied = readSatisfiedScore(record);
        if (satisfied !== undefined) {
            event.scores = { satisfied };
        }
    }
    return event;
}
function buildExperimentLog(row, entry) {
    const entryRecord = asRecord(entry) ?? {};
    const scores = buildExperimentScores(row.baseline, entry.scores);
    const metrics = { ...row.metrics };
    if (Number.isFinite(entry.durationMs)) {
        defineRecordEntry(metrics, "runtime_duration_ms", entry.durationMs);
    }
    return {
        input: redact({
            brief: readFirstString(entryRecord, ["brief"]),
            baseline: row.baseline,
            agent: row.agent,
            iteration: row.iteration,
        }),
        output: redact({
            diff_summary: entry.agentOutput,
            kept: entry.status === "keep",
        }),
        ...(Object.keys(scores).length === 0 ? {} : { scores }),
        ...(Object.keys(metrics).length === 0 ? {} : { metrics }),
        metadata: {
            ...(row.commit !== undefined ? { commit: row.commit } : {}),
            ...(row.reset !== undefined ? { reset: row.reset } : {}),
        },
    };
}
function buildPipelineMetrics(progress) {
    const metrics = {};
    const usage = progress.usage;
    if (usage !== undefined) {
        addMetric(metrics, "prompt_tokens", usage.inputTokens);
        addMetric(metrics, "completion_tokens", usage.outputTokens);
        if (isFiniteNonNegative(usage.inputTokens) && isFiniteNonNegative(usage.outputTokens)) {
            addMetric(metrics, "tokens", usage.inputTokens + usage.outputTokens);
        }
        addMetric(metrics, "prompt_cached_tokens", usage.cachedTokens);
    }
    addMetric(metrics, "durationMs", progress.durationMs);
    return metrics;
}
function buildExperimentScores(baseline, scores) {
    const result = finiteNumberRecord(scores) ?? {};
    const delta = sumDelta(baseline, scores);
    if (delta !== undefined) {
        defineRecordEntry(result, "aggregate_delta", delta);
    }
    return result;
}
function sumDelta(baseline, scores) {
    if (baseline === undefined || scores === undefined) {
        return undefined;
    }
    let delta = 0;
    let count = 0;
    for (const [name, value] of Object.entries(scores)) {
        const base = baseline[name];
        if (Number.isFinite(value) && Number.isFinite(base)) {
            delta += value - base;
            count += 1;
        }
    }
    return count === 0 ? undefined : delta;
}
function readSatisfiedScore(record) {
    const verdict = record?.verdict ?? record?.satisfied;
    if (typeof verdict === "boolean") {
        return verdict ? 1 : 0;
    }
    if (typeof verdict === "number" && Number.isFinite(verdict)) {
        return verdict > 0 ? 1 : 0;
    }
    if (typeof verdict !== "string") {
        return undefined;
    }
    const normalized = verdict.trim().toLowerCase();
    if (["satisfied", "satisfy", "pass", "passed", "approve", "approved", "true", "yes"].includes(normalized)) {
        return 1;
    }
    if (["unsatisfied", "fail", "failed", "reject", "rejected", "false", "no"].includes(normalized)) {
        return 0;
    }
    return undefined;
}
function pipelineKey(progress) {
    return JSON.stringify([
        progress.taskId,
        progress.stepName ?? "",
        progress.phase ?? "",
        progress.taskIndex,
        progress.stepIndex ?? null,
    ]);
}
function readPipelineStep(progress) {
    return progress.stepName ?? progress.phase ?? progress.taskTitle;
}
function readPipelineIndex(progress) {
    return progress.stepIndex ?? progress.taskIndex;
}
function readLatestRow(rows) {
    let latest;
    for (const row of rows.values()) {
        if (latest === undefined || row.iteration > latest.iteration) {
            latest = row;
        }
    }
    return latest;
}
function asSpanParent(value) {
    const span = value;
    if (span === undefined || typeof span.startSpan !== "function") {
        throw new Error("Braintrust span parent unavailable");
    }
    return span;
}
function asRecord(value) {
    return typeof value === "object" && value !== null
        ? value
        : undefined;
}
function readFirstString(record, keys) {
    const value = readFirstValue(record, keys);
    return typeof value === "string" ? value : undefined;
}
function readFirstValue(record, keys) {
    for (const key of keys) {
        if (Object.hasOwn(record, key)) {
            return record[key];
        }
    }
    return undefined;
}
function addMetric(metrics, key, value) {
    if (value !== undefined && isFiniteNonNegative(value)) {
        metrics[key] = value;
    }
}
function isFiniteNonNegative(value) {
    return Number.isFinite(value) && value >= 0;
}
function finiteNumberRecord(values) {
    if (values === undefined) {
        return undefined;
    }
    const result = {};
    for (const [key, value] of Object.entries(values)) {
        if (Number.isFinite(value)) {
            defineRecordEntry(result, key, value);
        }
    }
    return Object.keys(result).length === 0 ? undefined : result;
}
