"use client";
import { useCallback, useEffect, useMemo, useRef } from "react";
import { create } from "@bufbuild/protobuf";
import { GetSessionUsageReportInputSchema, } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/io_pb";
import { useStigmer } from "../hooks";
import { isTerminalPhase } from "../execution/execution-phases";
import { useFetch } from "../internal/useFetch";
/**
 * Poll cadence for the session usage report while an execution is in flight.
 * The proxy writes one authoritative billing record per turn, so polling lets
 * the settled cost climb live during the run rather than only at completion.
 */
const ACTIVE_POLL_INTERVAL_MS = 2500;
const EMPTY = {
    totalCostUsd: 0,
    totalTokens: 0,
    inputTokens: 0,
    outputTokens: 0,
    cacheReadTokens: 0,
    cacheCreationTokens: 0,
    llmCallCount: 0,
    modelBreakdown: [],
    primaryModel: "",
    primaryProvider: "",
    hasUsage: false,
    isEstimated: false,
};
function microsToUsd(micros) {
    return Number(micros) / 1_000_000;
}
function mapModelUsage(m) {
    return {
        model: m.model,
        provider: m.provider,
        estimatedCostUsd: microsToUsd(m.billableCostMicros),
        inputTokens: Number(m.inputTokens),
        outputTokens: Number(m.outputTokens),
        cacheCreationTokens: Number(m.cacheCreationInputTokens),
        cacheReadTokens: Number(m.cacheReadInputTokens),
        callCount: m.callCount,
    };
}
function mapReport(report) {
    const agg = report.totalUsage;
    if (!agg)
        return EMPTY;
    const llmCallCount = agg.llmCallCount;
    const totalTokens = Number(agg.totalTokens);
    const billableCost = microsToUsd(agg.billableCostMicros);
    return {
        totalCostUsd: billableCost,
        totalTokens,
        inputTokens: Number(agg.inputTokens),
        outputTokens: Number(agg.outputTokens),
        cacheReadTokens: Number(agg.cacheReadInputTokens),
        cacheCreationTokens: Number(agg.cacheCreationInputTokens),
        llmCallCount,
        modelBreakdown: report.modelBreakdown.map(mapModelUsage),
        primaryModel: agg.primaryModel,
        primaryProvider: agg.primaryProvider,
        hasUsage: llmCallCount > 0 || totalTokens > 0 || billableCost > 0,
        isEstimated: report.isEstimated ?? false,
    };
}
/**
 * Aggregate streaming-reported usage from execution status objects.
 * Used as a fallback when the server-side session usage report is empty
 * (e.g., Cursor harness where the proxy does not capture main agent turns).
 */
function aggregateStreamingUsage(executions) {
    let inputTokens = 0;
    let outputTokens = 0;
    let cacheReadTokens = 0;
    let cacheCreationTokens = 0;
    let totalTokens = 0;
    let turnCount = 0;
    let estimatedCost = 0;
    let model = "";
    for (const exec of executions) {
        const ru = exec.status?.streamingUsage;
        if (!ru || ru.turnCount === 0)
            continue;
        inputTokens += Number(ru.inputTokens);
        outputTokens += Number(ru.outputTokens);
        cacheReadTokens += Number(ru.cacheReadTokens);
        cacheCreationTokens += Number(ru.cacheWriteTokens);
        totalTokens += Number(ru.totalTokens);
        turnCount += ru.turnCount;
        estimatedCost += ru.estimatedCostUsd;
        if (!model && ru.model)
            model = ru.model;
    }
    if (turnCount === 0)
        return EMPTY;
    return {
        totalCostUsd: estimatedCost,
        totalTokens,
        inputTokens,
        outputTokens,
        cacheReadTokens,
        cacheCreationTokens,
        llmCallCount: turnCount,
        modelBreakdown: model
            ? [
                {
                    model,
                    provider: "cursor",
                    estimatedCostUsd: estimatedCost,
                    inputTokens,
                    outputTokens,
                    cacheCreationTokens,
                    cacheReadTokens,
                    callCount: turnCount,
                },
            ]
            : [],
        primaryModel: model,
        primaryProvider: model ? "cursor" : "",
        hasUsage: true,
        isEstimated: true,
    };
}
/**
 * Usage hook for session-level cost and token aggregation.
 *
 * Uses exactly ONE data source at a time to avoid displaying a cost total
 * from one pipeline with a model breakdown from another:
 *
 * - When billing records exist (proxy-metered), the billing report is shown
 *   as authoritative (`isEstimated: false`).
 * - When execution is in-flight and only streaming data is available,
 *   streaming is shown with `isEstimated: true`.
 * - The two sources are never mixed in the same display.
 *
 * @param executions - All executions for a session (completed + active).
 */
export function useSessionUsage(executions) {
    const stigmer = useStigmer();
    const sessionId = useMemo(() => executions[0]?.spec?.sessionId ?? null, [executions]);
    // Any execution still in flight means more authoritative records are coming,
    // so the report must be polled to stay current during the run.
    const hasActiveExecution = useMemo(() => executions.some((e) => {
        const phase = e.status?.phase;
        return phase !== undefined && !isTerminalPhase(phase);
    }), [executions]);
    // Stable fetch identity so the poll timer in useFetch is not torn down and
    // recreated on every streaming re-render (which would prevent it firing).
    const fetchReport = useCallback(() => stigmer.agentExecution.getSessionUsageReport(create(GetSessionUsageReportInputSchema, { sessionId: sessionId })), [sessionId, stigmer]);
    const { data: report, refetch } = useFetch(sessionId ? fetchReport : null, [sessionId, stigmer], null, {
        cacheKey: sessionId ? `session-usage:${sessionId}` : undefined,
        refetchInterval: hasActiveExecution ? ACTIVE_POLL_INTERVAL_MS : false,
    });
    // When the last execution settles, poll stops — do one final fetch so the
    // authoritative total (incl. the final turn) replaces the live estimate
    // promptly instead of waiting for a remount.
    const wasActiveRef = useRef(hasActiveExecution);
    useEffect(() => {
        if (wasActiveRef.current && !hasActiveExecution) {
            refetch();
        }
        wasActiveRef.current = hasActiveExecution;
    }, [hasActiveExecution, refetch]);
    const streamingFallback = useMemo(() => aggregateStreamingUsage(executions), [executions]);
    return useMemo(() => {
        const billingReport = report ? mapReport(report) : EMPTY;
        // Authoritative proxy-metered report wins as soon as any record exists.
        // While an execution runs, per-turn records land continuously and polling
        // keeps this total fresh, so it is never shadowed by a stale snapshot.
        if (billingReport.hasUsage)
            return billingReport;
        // No billing record yet (e.g. before the first turn settles) — fall back
        // to the runner's display-only streaming estimate.
        if (streamingFallback.hasUsage)
            return streamingFallback;
        return EMPTY;
    }, [report, streamingFallback]);
}
//# sourceMappingURL=useSessionUsage.js.map