import z from "zod";
import { isMigrationChecklistActive, readMigrationChecklist, } from "../../../migration/migration-checklist.js";
import { sendUserMessageChannel } from "../../state-machine/helpers/peer.js";
import { createToolFactory, ToolCategory, ToolExecutionError, } from "../tools2/types.js";
import { checkChecklistCompletion, } from "./build-manage-checklist.js";
import { debugCache } from "./debug-cache.js";
export const buildFinalizeToolFactory = createToolFactory("build_finalize", ({ clark, services, }) => {
    const sendUserMessage = sendUserMessageChannel(clark, services.chatSessionStore);
    return {
        category: ToolCategory.GENERAL,
        description: `
      Finalize the build and complete the generation.
      You must call build_debug at least once before this will succeed.
    `,
        inputSchema: z.object({
            summary: z
                .string()
                .describe("A no longer than 80 words summary of the build to be returned to the reader. Make sure it is correctly formatted so it can be rendered by streamdown in the ui."),
            adviceToAgents: z
                .string()
                .optional()
                .describe("Advice for other agents to be used in subsequent LLM calls"),
            relevantFilePaths: z.array(z.string()).optional(),
            goals: z.array(z.string()).optional().describe("The user's goals"),
            decisions: z
                .array(z.string())
                .optional()
                .describe("The agent's decisions"),
        }),
        outputSchema: BuildFinalizeSuccessSchema,
        execute: async ({ summary, relevantFilePaths, goals, decisions, adviceToAgents, }) => {
            // Check for failed API builds - must be fixed before finalizing
            if (clark.context.failedApiBuilds?.size) {
                const failedApisList = Array.from(clark.context.failedApiBuilds)
                    .map((api) => `- ${api}`)
                    .join("\n");
                throw new BuildFinalizeError({
                    status: "failed_api_builds",
                    message: `Cannot finalize: ${clark.context.failedApiBuilds.size} API build(s) failed and have not been successfully rebuilt:\n\n${failedApisList}\n\nThese changes exist only in scratch files and will NOT be saved.`,
                    instruction: "Fix the build errors and call buildApi successfully for each failed API, then call build_finalize again",
                });
            }
            // Require testApi for every SDK API before finalizing (same policy as build_debug warnings)
            const untestedForFinalize = clark.context.apiTestingState?.untested;
            if (untestedForFinalize?.size) {
                const untestedList = Array.from(untestedForFinalize)
                    .map((api) => `- ${api}`)
                    .join("\n");
                throw new BuildFinalizeError({
                    status: "apis_untested",
                    message: `Cannot finalize: ${untestedForFinalize.size} API(s) have not been tested yet:\n\n${untestedList}`,
                    instruction: "Call testApi for each untested API, then run build_debug again before build_finalize",
                });
            }
            const migrationChecklist = await readMigrationChecklist(services.appRootDirPath);
            if (migrationChecklist) {
                const pendingMigrationTasks = migrationChecklist.items.filter((item) => item.status === "pending" || item.status === "in_progress");
                if (pendingMigrationTasks.length > 0) {
                    const pendingTasksList = pendingMigrationTasks
                        .map((task) => `- ${task.content}`)
                        .join("\n");
                    throw new BuildFinalizeError({
                        status: "checklist_incomplete",
                        message: `Migration checklist still has incomplete items. Please complete all tasks before finalizing:\n\n${pendingTasksList}`,
                        instruction: "Please complete all migration checklist items using build_manageChecklist, then call build_finalize again",
                        pendingTasks: pendingMigrationTasks,
                    });
                }
            }
            else {
                // Check if checklist is complete
                const checklistStatus = checkChecklistCompletion(clark);
                if (!checklistStatus.complete) {
                    const pendingTasksList = checklistStatus.pendingTasks
                        .map((task) => `- ${task.content}`)
                        .join("\n");
                    throw new BuildFinalizeError({
                        status: "checklist_incomplete",
                        message: `You have incomplete tasks in your checklist. Please complete all tasks before finalizing:\n\n${pendingTasksList}`,
                        instruction: "Please complete all checklist items using build_manageChecklist, then call build_finalize again",
                        pendingTasks: checklistStatus.pendingTasks,
                    });
                }
            }
            const hasDiff = await services.draftInterface.hasLocalDraftChanges();
            if (!clark.context.debugToolCalled && hasDiff) {
                throw new BuildFinalizeError({
                    status: "debug_required",
                    message: "You must call build_debug at least once before finalizing to ensure there are no lint or type errors",
                    instruction: "Please call build_debug first, fix any errors found, then call build_finalize again",
                });
            }
            // Optional: Check if debug was called recently (within reasonable time)
            const debugAge = Date.now() - (clark.context.lastDebugTimestamp || 0);
            const maxDebugAge = 5 * 60 * 1000; // 5 minutes
            if (debugAge > maxDebugAge) {
                throw new BuildFinalizeError({
                    status: "debug_stale",
                    message: "The last debug check was performed too long ago. Please run build_debug again to ensure current code is error-free",
                    instruction: "Please call build_debug again, then call build_finalize",
                });
            }
            // Check if the last debug run had type errors
            if (clark.context.lastDebugHadTypeErrors) {
                throw new BuildFinalizeError({
                    status: "type_errors_unresolved",
                    message: "The last debug check found TypeScript errors that have not been fixed. Please fix the type errors and run build_debug again.",
                    instruction: "Fix the TypeScript errors reported by build_debug, then run build_debug again to verify they are resolved before calling build_finalize",
                });
            }
            // Check for unresolved disappearing errors against CURRENT errors
            const errorTracker = debugCache.getErrorHistoryTracker();
            const getTopFrame = (stack) => {
                if (!stack)
                    return "";
                const text = String(stack);
                const firstLine = text.split("\n").find((l) => l.trim().length > 0);
                return firstLine ? firstLine.trim() : "";
            };
            const getFingerprintKey = (e) => {
                const componentType = e?.identifier?.type || "";
                const instanceId = e?.identifier?.instanceId;
                const sourceId = e?.identifier?.sourceId;
                const sourceIdStr = sourceId ? String(JSON.stringify(sourceId)) : "";
                const errorString = String(e?.error ?? "");
                const errorMessage = errorString.split("\n")[0] || errorString;
                const topFrame = getTopFrame(e?.errorInfo?.componentStack);
                const fingerprintParts = [
                    errorMessage.trim(),
                    componentType,
                    instanceId ?? sourceIdStr,
                    topFrame,
                ].filter(Boolean);
                const fallback = `${errorMessage}::${componentType}`;
                const joined = fingerprintParts.join("::");
                const key = joined || fallback;
                return key;
            };
            const currentErrorKeys = new Set();
            for (const e of (clark.context.runtimeErrors ?? [])) {
                currentErrorKeys.add(getFingerprintKey(e));
            }
            const disappeared = errorTracker.getUnresolvedDisappearances(currentErrorKeys);
            if (disappeared.length > 0) {
                const errorList = disappeared
                    .map((h) => `- ${h.errorKey}`)
                    .join("\n");
                throw new BuildFinalizeError({
                    status: "unresolved_errors",
                    message: `Runtime errors were detected earlier but have disappeared without clear fixes:\n\n${errorList}\n\nThese may resurface under different conditions.`,
                    instruction: "Please review code that accesses API data and add defensive patterns (optional chaining, fallback values), then run build_debug again to confirm all errors are resolved.",
                });
            }
            // Proceed with normal finalization
            await sendUserMessage({
                role: "assistant",
                type: "text",
                content: summary,
            });
            await services.chatSessionStore.recordSummary({
                summary,
                relevantFilePaths: relevantFilePaths || [],
                goals: goals || [],
                decisions: decisions || [],
                adviceToAgents: adviceToAgents || "",
            });
            const preserveMigrationChecklist = migrationChecklist !== null ||
                (await isMigrationChecklistActive(services.appRootDirPath));
            // Clear ephemeral per-turn checklist for regular Clark sessions.
            // Migration checklist state is persisted on disk and should stay
            // visible until explicit completion.
            if (!preserveMigrationChecklist &&
                clark.context.checklist?.items.length) {
                clark.context.checklist = {
                    items: [],
                    lastUpdated: new Date().toISOString(),
                };
                // Notify UI that checklist should be cleared
                await services.appShell.sendMessage({
                    type: "checklist_cleared",
                    payload: {},
                });
            }
            // Clear build system errors and failed API tracking since finalization succeeded
            clark.updateContext((context) => ({
                ...context,
                buildSystemErrors: [],
                failedApiBuilds: new Set(),
                apiTestingState: undefined,
                sdkApiRegistryEntryPoints: undefined,
            }));
            // Invalidate context errors cache since build system errors changed
            debugCache.invalidateContextErrors();
            return {
                status: "success",
                summary,
                relevantFilePaths,
                goals,
                decisions,
            };
        },
        toModelOutput: ({ output }) => {
            return { type: "text", value: output.summary };
        },
        getSummary: (_input, output) => {
            return { output: output.status === "success" ? "Completed" : "Failed" };
        },
    };
});
const BuildFinalizeSuccessSchema = z.object({
    status: z.literal("success"),
    summary: z.string(),
    relevantFilePaths: z.array(z.string()).optional(),
    goals: z.array(z.string()).optional(),
    decisions: z.array(z.string()).optional(),
});
class BuildFinalizeError extends ToolExecutionError {
    status;
    instruction;
    pendingTasks;
    constructor(options) {
        super(options.message, {
            toolName: "build_finalize",
            input: {},
            context: {},
            severity: "warn",
        });
        this.status = options.status;
        this.instruction = options.instruction;
        this.pendingTasks = options.pendingTasks;
    }
}
//# sourceMappingURL=build-finalize.js.map