import { generateText } from "ai";
import { AiGenerationState } from "@superblocksteam/library-shared/types";
import { CLARK_SUMMARY_HEADER_CONTENT } from "../../const.js";
import { traceLLM } from "../../llmobs/helpers.js";
import { LLMObsTracer } from "../../llmobs/index.js";
import { AGENT_NEEDS_USER_INPUT, V3_AGENT_FINISHED, RUNTIME_REVIEW_FINISHED, SERVICE_STARTED_WITH_DRAFT, } from "../clark-fsm.js";
import { sendUserGenerationStateChannel, sendUserMessageChannel, } from "../helpers/peer.js";
import { getNextSteps } from "./next-steps.js";
/**
 * Converts Clark context workflow metrics to AiWorkflowSummary format for submission.
 */
function createWorkflowSummary(promptId, workflowMetrics) {
    const completedAt = new Date().toISOString();
    const startedAt = workflowMetrics.workflowStartedAt;
    return {
        promptId: promptId,
        attributes: {
            tool_call_count: workflowMetrics.toolCallCount,
            step_count: workflowMetrics.stepCount,
            lines_added: workflowMetrics.linesAdded,
            lines_edited: workflowMetrics.linesEdited,
            lines_removed: workflowMetrics.linesRemoved,
            workflow_started_at: startedAt,
            workflow_completed_at: completedAt,
        },
    };
}
/**
 * Log accumulated credit usage for the prompt and warn if it exceeds the threshold.
 */
async function checkPromptCost(promptId, workflowMetrics, chatSessionStore, promptUsageAlarmThreshold, logger) {
    let totalCreditsUsed = 0;
    try {
        totalCreditsUsed = await chatSessionStore.calculatePromptCost(promptId, workflowMetrics.llmUsage);
    }
    catch (error) {
        logger.warn("[billing:prompt-cost] Failed to calculate prompt credits", {
            promptId,
            error: error instanceof Error ? error.message : String(error),
        });
        return;
    }
    const costSummary = {
        promptId,
        totalCreditsUsed,
        llmCallCount: workflowMetrics.llmCallCount,
        threshold: promptUsageAlarmThreshold,
    };
    if (totalCreditsUsed > promptUsageAlarmThreshold) {
        logger.warn(`[billing:prompt-cost] Prompt credit usage ${totalCreditsUsed} exceeds threshold of ${promptUsageAlarmThreshold}`, costSummary);
    }
    else {
        logger.info(`[billing:prompt-cost] Prompt cost summary: ${totalCreditsUsed} credits across ${workflowMetrics.llmCallCount} LLM calls`, costSummary);
    }
}
export const doAwaitingUser = (clark, { signals, chatSessionStore, llmProvider, clarkProfiler, features, }) => {
    const sendUsergenerationState = sendUserGenerationStateChannel(clark);
    const sendUserMessage = sendUserMessageChannel(clark, chatSessionStore);
    const emitNextSteps = async () => {
        // Capture the abort signal at entry so we always check the original
        // controller, not a replacement created by a new prompt.
        const abortSignal = clark.context.abortController?.signal;
        // emitNextSteps runs once per Clark prompt-to-done cycle at terminal
        // states (V3_AGENT_FINISHED / RUNTIME_REVIEW_FINISHED) and is awaited
        // by the caller before emitting `generationCompleted`, so its total
        // wall time adds to the perceived "Clark is done" latency.  Track
        // LLM + persist + peer send so p50/p95 can be monitored in production
        // before broad ramp of the feature flag.
        //
        // Duration is emitted from a `finally` block so aborted runs (user
        // cancels, new prompt preempts) are also recorded — those are the most
        // informative samples for latency investigations.
        const startedAt = performance.now();
        let stepCount = 0;
        let caughtError;
        try {
            if (abortSignal?.aborted)
                return;
            clark.logger.info("[awaiting-user] emitNextSteps starting");
            const model = llmProvider.modelForTask("summarizeMessages");
            const nextSteps = await getNextSteps({
                model,
                chatSessionStore,
                logger: clark.logger,
                abortSignal,
            });
            stepCount = nextSteps.length;
            if (abortSignal?.aborted)
                return;
            if (nextSteps.length > 0) {
                await sendUserMessage({
                    type: "next_steps",
                    role: "assistant",
                    content: "",
                    nextSteps,
                }, 0, false, { persistBeforeDeliver: true });
            }
        }
        catch (err) {
            caughtError = err;
        }
        finally {
            // Round to 2 decimals but keep numeric so downstream tooling (Datadog
            // facets, alerting) does not have to parse strings.
            const durationMs = Math.round((performance.now() - startedAt) * 100) / 100;
            const aborted = abortSignal?.aborted === true;
            if (caughtError) {
                clark.logger.warn("[awaiting-user] Next steps generation failed; skipping", {
                    durationMs,
                    stepCount,
                    aborted,
                    error: caughtError instanceof Error
                        ? { name: caughtError.name, message: caughtError.message }
                        : String(caughtError),
                });
            }
            else {
                clark.logger.info("[awaiting-user] emitNextSteps completed", {
                    durationMs,
                    stepCount,
                    aborted,
                });
            }
        }
    };
    return async ({ event }) => {
        switch (event.type) {
            case AGENT_NEEDS_USER_INPUT: {
                const { hasSuggestions = false } = clark.context;
                const { advice } = event;
                const { promptId, workflowMetrics } = clark.context;
                clark.logger.info("[awaiting-user] AGENT_NEEDS_USER_INPUT - returning to user", {
                    hasSuggestions,
                    currentMode: clark.context.currentMode,
                    hasAdvice: !!advice,
                    promptId,
                    hasWorkflowMetrics: Boolean(workflowMetrics),
                });
                signals.emit("generationCompleted", hasSuggestions);
                await clarkProfiler?.endSession();
                // Submit workflow summary metrics and check prompt cost
                if (promptId && workflowMetrics) {
                    await checkPromptCost(promptId, workflowMetrics, chatSessionStore, features.promptUsageAlarmThreshold, clark.logger);
                    const submitStart = performance.now();
                    clark.logger.info("[awaiting-user] Starting submitWorkflowSummary");
                    try {
                        await chatSessionStore.submitWorkflowSummary(createWorkflowSummary(promptId, workflowMetrics));
                        clark.logger.info(`[awaiting-user] submitWorkflowSummary completed in ${(performance.now() - submitStart).toFixed(2)}ms`);
                    }
                    catch (err) {
                        clark.logger.warn("[awaiting-user] submitWorkflowSummary failed; continuing", {
                            durationMs: (performance.now() - submitStart).toFixed(2),
                            promptId,
                            error: err instanceof Error
                                ? { name: err.name, message: err.message }
                                : String(err),
                        });
                    }
                }
                if (advice) {
                    void sendUserMessage({
                        type: "text",
                        role: "assistant",
                        content: advice,
                    });
                }
                clark.logger.info("[awaiting-user] Emitting generation state AWAITING_USER_INPUT", {
                    hasSuggestions,
                    promptId,
                    hasAdvice: Boolean(advice),
                });
                void sendUsergenerationState({
                    state: AiGenerationState.AWAITING_USER_INPUT,
                    hasSuggestions,
                });
                break;
            }
            case V3_AGENT_FINISHED: {
                const { hasSuggestions = false } = clark.context;
                clark.logger.info("[awaiting-user] V3_AGENT_FINISHED - returning to user, emitting generationCompleted", {
                    hasSuggestions,
                    willTriggerUpload: hasSuggestions,
                    currentMode: clark.context.currentMode,
                });
                await clarkProfiler?.endSession();
                // Submit workflow summary metrics and check prompt cost
                const { promptId, workflowMetrics } = clark.context;
                if (promptId && workflowMetrics) {
                    await checkPromptCost(promptId, workflowMetrics, chatSessionStore, features.promptUsageAlarmThreshold, clark.logger);
                    await chatSessionStore.submitWorkflowSummary(createWorkflowSummary(promptId, workflowMetrics));
                }
                // Run next-steps LLM call BEFORE emitting generationCompleted.
                // generationCompleted triggers async directory uploads that can
                // clear the peer (via resetContext/clearPeerConnection), causing
                // sendUserMessage to silently drop the next_steps message.
                if (features.suggestWhatsNext) {
                    await emitNextSteps();
                }
                // Now safe to signal — upload/full-reload race can no longer
                // interfere with next-steps delivery.
                signals.emit("generationCompleted", hasSuggestions);
                void sendUsergenerationState({
                    state: AiGenerationState.COMPLETED,
                    hasSuggestions: true,
                });
                break;
            }
            case RUNTIME_REVIEW_FINISHED: {
                const { hasSuggestions = false, summaryMessages = [] } = clark.context;
                clark.logger.info("[awaiting-user] RUNTIME_REVIEW_FINISHED - emitting generationCompleted", {
                    hasSuggestions,
                    willTriggerUpload: hasSuggestions,
                });
                await clarkProfiler?.endSession();
                // Submit workflow summary metrics and check prompt cost
                const { promptId, workflowMetrics } = clark.context;
                if (promptId && workflowMetrics) {
                    await checkPromptCost(promptId, workflowMetrics, chatSessionStore, features.promptUsageAlarmThreshold, clark.logger);
                    await chatSessionStore.submitWorkflowSummary(createWorkflowSummary(promptId, workflowMetrics));
                }
                if (summaryMessages.length > 0) {
                    void sendUserMessage({
                        type: "text",
                        role: "assistant",
                        group: "clark",
                        content: "\n---\n",
                    });
                    void sendUserMessage({
                        type: "text",
                        role: "assistant",
                        group: "clark",
                        content: CLARK_SUMMARY_HEADER_CONTENT,
                    });
                    if (clark.context.usedDebugging) {
                        void sendUsergenerationState({
                            state: AiGenerationState.SUMMARIZING,
                            hasSuggestions,
                        });
                        // Start summary generation profiling
                        clarkProfiler.getProfiler().startFrame("summary_generation");
                        clarkProfiler.startLLMWaiting();
                        const finalSummary = await getFinalSummary(llmProvider, chatSessionStore, summaryMessages, clark.tracer, clark.context.abortController?.signal);
                        // End profiling spans
                        clarkProfiler.endFrame(); // End waiting_on_llm
                        clarkProfiler.endFrame(); // End summary_generation
                        void sendUserMessage({
                            type: "text",
                            role: "assistant",
                            content: finalSummary,
                        });
                    }
                    else {
                        for (const message of summaryMessages) {
                            void sendUserMessage({
                                type: "text",
                                role: "assistant",
                                content: message.content,
                            });
                        }
                    }
                }
                // Run next-steps LLM call BEFORE emitting generationCompleted.
                // See V3_AGENT_FINISHED comment for rationale.
                if (features.suggestWhatsNext) {
                    await emitNextSteps();
                }
                signals.emit("generationCompleted", hasSuggestions);
                void sendUsergenerationState({
                    state: AiGenerationState.COMPLETED,
                    hasSuggestions,
                });
                break;
            }
            case SERVICE_STARTED_WITH_DRAFT:
                break;
            default:
                throw new Error(`Unhandled event: ${event}`);
        }
    };
};
const getFinalSummary = async (llmProvider, chatSessionStore, summaryMessages, llmobs = LLMObsTracer.instance, abortSignal) => {
    const lastUserMessage = chatSessionStore.getLastUserMessage();
    const initialMessages = summaryMessages.filter((msg) => !msg.isDebugging);
    const debuggingMessages = summaryMessages.filter((msg) => msg.isDebugging);
    const userPrompt = lastUserMessage?.content || "User request";
    const prompt = `Create a concise, human-readable summary of what was accomplished based on the following conversation from an AI assistant helping a user with their Superblocks application.

USER'S REQUEST:
"${userPrompt}"

ASSISTANT'S ACTIONS:
The messages below describe what the AI assistant did in response to the user's request. Some messages are from the initial response, and some are from debugging/fixing issues that occurred afterward.

Initial Response Messages:
${initialMessages.map((msg) => `- ${msg.content}`).join("\n")}

${debuggingMessages.length > 0
        ? `\nDebugging/Fix Messages:
${debuggingMessages.map((msg) => `- ${msg.content}`).join("\n")}`
        : ""}

Please create a BRIEF summary that:
1. Explains what was changed or accomplished in clear, non-technical language
2. Groups related changes together logically
3. If there were debugging fixes, weave these changes into the summary; do not identify them as "debugging"
4. Is written from the perspective of what was done for the user
5. Focuses on the end result rather than technical implementation details
6. CRITICAL: Matches the verbosity and tone of both the user's original request and the assistant's initial response messages - if the user was brief, keep it concise; if they were detailed, provide more context

Create a summary of what was accomplished:`;
    const model = llmProvider.modelForTask("summarizeMessages");
    const summary = await traceLLM("getFinalSummary", model.modelId, model.provider, async () => {
        return generateText({
            model,
            prompt,
            abortSignal,
        });
    }, {}, llmobs);
    return summary.text;
};
//# sourceMappingURL=awaiting-user.js.map