import { createRandomId } from "@best-agent/foundation";
import type { ItemId, RunPhaseName, Turn } from "@best-agent/protocol";
import type { AgentProfile } from "../../agent/profile-registry.js";
import type { PreparedToolInvocation } from "../../tool/registry.js";
import type { ExecuteToolInvocationOptions } from "../agent-runtime-tool-execution-types.js";
import type { ToolResumeContext } from "../checkpoint-codec.js";
import type {
  PersistTerminalToolFailure,
  ToolExecutionRuntime,
  ToolShellDebugBase,
} from "./tool-execution-types.js";

export async function appendToolReasoningPrelude(input: {
  options: ExecuteToolInvocationOptions;
  profile: AgentProfile;
  reasoning?: string;
  runtime: ToolExecutionRuntime;
  toolName: string;
  turn: Turn;
}): Promise<void> {
  if (input.options.skipPrelude || !input.reasoning || input.reasoning.trim().length === 0) {
    return;
  }
  await input.runtime.items.pushItems([
    {
      id: createRandomId<"ItemId">("item"),
      threadId: input.turn.threadId,
      turnId: input.turn.id,
      createdAt: new Date().toISOString(),
      kind: "reasoning",
      status: "draft",
      summary: `The ${input.profile.name} profile selected tool ${input.toolName}: ${input.reasoning.trim()}`,
    },
  ]);
}

export async function prepareToolInvocationForExecution(input: {
  activeSkillIds: string[];
  approvalTurn?: Turn;
  currentPhaseName?: RunPhaseName;
  options: ExecuteToolInvocationOptions;
  persistTerminalToolFailure: PersistTerminalToolFailure;
  previewCallId?: ItemId;
  profile: AgentProfile;
  reasoning?: string;
  runtime: ToolExecutionRuntime;
  shellDebugBase?: ToolShellDebugBase;
  toolInput: ToolResumeContext["toolInput"];
  toolName: string;
  turn: Turn;
}): Promise<
  | {
      kind: "prepared";
      prepared: PreparedToolInvocation;
      preparedToolResumeContext: ToolResumeContext;
      toolResumeContext: ToolResumeContext;
    }
  | { kind: "replayed"; output: string }
  | { kind: "terminal-failure"; output: string }
> {
  const {
    activeSkillIds,
    approvalTurn,
    currentPhaseName,
    options,
    persistTerminalToolFailure,
    previewCallId,
    profile,
    reasoning,
    runtime,
    shellDebugBase,
    toolInput,
    toolName,
    turn,
  } = input;
  const toolContext = runtime.preparedTools.createToolContext(turn, {
    heartbeat: () => {},
    toolInputMode: options.toolInputMode,
  });
  let prepared: PreparedToolInvocation;
  try {
    if (shellDebugBase) {
      runtime.tracing.logShellToolDebug(runtime.cwd, "shell.prepare.start", shellDebugBase);
    }
    prepared = await runtime.preparedTools.tools.prepare(toolName, toolInput, toolContext);
    if (shellDebugBase) {
      runtime.tracing.logShellToolDebug(runtime.cwd, "shell.prepare.done", {
        ...shellDebugBase,
        capabilityRequests: prepared.capabilityRequests.map((request) =>
          runtime.tracing.describeCapabilityRequestForDebugLog(request),
        ),
        displayInput: runtime.formatting.previewDebugText(prepared.displayInput, 500),
        normalizedInput: runtime.formatting.previewToolInputForDebugLog(prepared.normalizedInput),
      });
    }
  } catch (error) {
    if (shellDebugBase) {
      runtime.tracing.logShellToolDebug(runtime.cwd, "shell.prepare.error", {
        ...shellDebugBase,
        error: runtime.tracing.describeErrorForDebugLog(error),
      });
    }
    const fallbackToolCallId =
      options.skipPrelude && options.resumeContext?.toolCallId
        ? options.resumeContext.toolCallId
        : previewCallId;
    const failedHistoryInput = runtime.formatting.renderToolInputForProviderHistory(
      toolName,
      toolInput,
    );
    const toolCallItem = runtime.toolCalls.createToolCallItem(
      turn,
      toolName,
      runtime.formatting.formatToolInputForPrompt(failedHistoryInput),
      fallbackToolCallId,
      runtime.formatting.isContentBearingToolInputForCheckpoint(toolName, toolInput)
        ? failedHistoryInput
        : undefined,
      options.resumeContext?.providerToolName,
    );
    if (!options.skipPrelude) {
      await runtime.items.pushItems([toolCallItem]);
    }
    const failureResumeContext: ToolResumeContext = options.resumeContext ?? {
      activeSkillIds,
      availableTools: [],
      approvalTurnId: approvalTurn?.id,
      cacheControl: undefined,
      messages: [],
      mode: "tool-plan",
      phaseName: currentPhaseName,
      profileName: profile.name,
      requiredToolConstraint: undefined,
      systemPrompt: undefined,
      toolCallId: toolCallItem.callId,
      toolInput,
      toolName,
      toolStepCount: 0,
      ...(reasoning ? { reasoning } : {}),
    };
    failureResumeContext.toolCallId = toolCallItem.callId;
    failureResumeContext.toolInput = toolInput;
    failureResumeContext.toolName = toolName;
    const failureOutput = await persistTerminalToolFailure(
      toolCallItem.callId,
      toolInput,
      failureResumeContext,
      error,
    );
    if (failureOutput !== undefined) {
      return { kind: "terminal-failure", output: failureOutput };
    }
    throw error;
  }
  const toolResumeContext = options.resumeContext ?? {
    activeSkillIds,
    availableTools: [],
    approvalTurnId: approvalTurn?.id,
    cacheControl: undefined,
    messages: [],
    mode: "tool-plan",
    phaseName: currentPhaseName,
    profileName: profile.name,
    requiredToolConstraint: undefined,
    systemPrompt: undefined,
    toolCallId: previewCallId ?? createRandomId<"ItemId">("item"),
    toolInput: prepared.normalizedInput,
    toolName,
    toolStepCount: 0,
    ...(reasoning ? { reasoning } : {}),
  };
  toolResumeContext.toolInput = prepared.normalizedInput;
  const preparedToolResumeContext: ToolResumeContext = {
    ...toolResumeContext,
    toolInput: prepared.normalizedInput,
  };
  if (options.customLoopReplay) {
    const replayed = runtime.replay.takeCustomLoopToolReplayResult(
      options.customLoopReplay,
      toolName,
      prepared.normalizedInput,
    );
    if (replayed !== undefined) {
      return { kind: "replayed", output: replayed };
    }
  }
  return {
    kind: "prepared",
    prepared,
    preparedToolResumeContext,
    toolResumeContext,
  };
}
