/**
 * Content-block mapping helpers and streaming state tracker.
 *
 * Translates the Claude Agent SDK's `BetaRawMessageStreamEvent` sequence
 * into NERF's `AssistantMessageEvent` deltas for incremental TUI rendering.
 */
// ---------------------------------------------------------------------------
// Content-block mapping helpers
// ---------------------------------------------------------------------------
/**
 * Convert a single BetaContentBlock to the corresponding NERF content type.
 */
export function mapContentBlock(block) {
    switch (block.type) {
        case "text":
            return { type: "text", text: block.text };
        case "thinking":
            return {
                type: "thinking",
                thinking: block.thinking,
                ...(block.signature ? { thinkingSignature: block.signature } : {}),
            };
        case "tool_use":
            return {
                type: "toolCall",
                id: block.id,
                name: block.name,
                arguments: block.input,
            };
        case "server_tool_use":
            return {
                type: "serverToolUse",
                id: block.id,
                name: block.name,
                input: block.input,
            };
        case "web_search_tool_result":
            return {
                type: "webSearchResult",
                toolUseId: block.tool_use_id,
                content: block.content,
            };
        default: {
            const unknown = block;
            return { type: "text", text: `[unknown content block: ${JSON.stringify(unknown)}]` };
        }
    }
}
export function mapStopReason(reason) {
    switch (reason) {
        case "end_turn":
        case "stop_sequence":
            return "stop";
        case "max_tokens":
            return "length";
        case "tool_use":
            return "toolUse";
        default:
            return "stop";
    }
}
/**
 * Convert SDK usage + total_cost_usd into NERF's Usage shape.
 *
 * The SDK does not break cost down per-bucket, so all cost is
 * attributed to `cost.total`.
 */
export function mapUsage(sdkUsage, totalCostUsd) {
    return {
        input: sdkUsage.input_tokens,
        output: sdkUsage.output_tokens,
        cacheRead: sdkUsage.cache_read_input_tokens,
        cacheWrite: sdkUsage.cache_creation_input_tokens,
        totalTokens: sdkUsage.input_tokens +
            sdkUsage.output_tokens +
            sdkUsage.cache_read_input_tokens +
            sdkUsage.cache_creation_input_tokens,
        cost: {
            input: 0,
            output: 0,
            cacheRead: 0,
            cacheWrite: 0,
            total: totalCostUsd,
        },
    };
}
// ---------------------------------------------------------------------------
// Zero-cost usage constant
// ---------------------------------------------------------------------------
export const ZERO_USAGE = {
    input: 0,
    output: 0,
    cacheRead: 0,
    cacheWrite: 0,
    totalTokens: 0,
    cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
};
// ---------------------------------------------------------------------------
// Streaming partial-message state tracker
// ---------------------------------------------------------------------------
/**
 * Mutable accumulator that tracks the partial AssistantMessage being built
 * from a sequence of stream_event messages. Produces AssistantMessageEvent
 * deltas that the TUI can render incrementally.
 */
export class PartialMessageBuilder {
    partial;
    /** Map from stream-event `index` to our content array index. */
    indexMap = new Map();
    /** Accumulated JSON input string per tool_use block (keyed by stream index). */
    toolJsonAccum = new Map();
    constructor(model) {
        this.partial = {
            role: "assistant",
            content: [],
            api: "anthropic-messages",
            provider: "claude-code",
            model,
            usage: { ...ZERO_USAGE },
            stopReason: "stop",
            timestamp: Date.now(),
        };
    }
    get message() {
        return this.partial;
    }
    /**
     * Feed a BetaRawMessageStreamEvent and return the corresponding
     * AssistantMessageEvent (or null if the event is not mapped).
     */
    handleEvent(event) {
        const streamIndex = event.index ?? 0;
        switch (event.type) {
            // ---- Block start ----
            case "content_block_start": {
                const block = event.content_block;
                if (!block)
                    return null;
                const contentIndex = this.partial.content.length;
                this.indexMap.set(streamIndex, contentIndex);
                if (block.type === "text") {
                    this.partial.content.push({ type: "text", text: "" });
                    return { type: "text_start", contentIndex, partial: this.partial };
                }
                if (block.type === "thinking") {
                    this.partial.content.push({ type: "thinking", thinking: "" });
                    return { type: "thinking_start", contentIndex, partial: this.partial };
                }
                if (block.type === "tool_use") {
                    this.toolJsonAccum.set(streamIndex, "");
                    this.partial.content.push({
                        type: "toolCall",
                        id: block.id,
                        name: block.name,
                        arguments: {},
                    });
                    return { type: "toolcall_start", contentIndex, partial: this.partial };
                }
                if (block.type === "server_tool_use") {
                    this.partial.content.push({
                        type: "serverToolUse",
                        id: block.id,
                        name: block.name,
                        input: block.input,
                    });
                    return { type: "server_tool_use", contentIndex, partial: this.partial };
                }
                return null;
            }
            // ---- Block delta ----
            case "content_block_delta": {
                const contentIndex = this.indexMap.get(streamIndex);
                if (contentIndex === undefined)
                    return null;
                const delta = event.delta;
                if (!delta)
                    return null;
                if (delta.type === "text_delta" && typeof delta.text === "string") {
                    const existing = this.partial.content[contentIndex];
                    existing.text += delta.text;
                    return { type: "text_delta", contentIndex, delta: delta.text, partial: this.partial };
                }
                if (delta.type === "thinking_delta" && typeof delta.thinking === "string") {
                    const existing = this.partial.content[contentIndex];
                    existing.thinking += delta.thinking;
                    return { type: "thinking_delta", contentIndex, delta: delta.thinking, partial: this.partial };
                }
                if (delta.type === "input_json_delta" && typeof delta.partial_json === "string") {
                    const accum = (this.toolJsonAccum.get(streamIndex) ?? "") + delta.partial_json;
                    this.toolJsonAccum.set(streamIndex, accum);
                    return { type: "toolcall_delta", contentIndex, delta: delta.partial_json, partial: this.partial };
                }
                return null;
            }
            // ---- Block stop ----
            case "content_block_stop": {
                const contentIndex = this.indexMap.get(streamIndex);
                if (contentIndex === undefined)
                    return null;
                const block = this.partial.content[contentIndex];
                if (block.type === "text") {
                    return { type: "text_end", contentIndex, content: block.text, partial: this.partial };
                }
                if (block.type === "thinking") {
                    return { type: "thinking_end", contentIndex, content: block.thinking, partial: this.partial };
                }
                if (block.type === "toolCall") {
                    const jsonStr = this.toolJsonAccum.get(streamIndex) ?? "{}";
                    try {
                        block.arguments = JSON.parse(jsonStr);
                    }
                    catch {
                        // Stream was truncated mid-tool-call — JSON is garbage.
                        // Preserve the raw string for diagnostics but signal the
                        // malformation explicitly so downstream consumers can
                        // distinguish this from a healthy tool completion (#2574).
                        block.arguments = { _raw: jsonStr };
                        return { type: "toolcall_end", contentIndex, toolCall: block, partial: this.partial, malformedArguments: true };
                    }
                    return { type: "toolcall_end", contentIndex, toolCall: block, partial: this.partial };
                }
                return null;
            }
            default:
                return null;
        }
    }
}
