import type { FailureRecoverability, FailureScope } from "@best-agent/protocol";
import { normalizeWhitespace, truncateText } from "@best-agent/foundation";
import { truncateForReasoning } from "./text.js";

export type AgentVisibleRuntimeErrorInput = {
  code: string;
  details?: readonly string[];
  message: string;
};

export function formatAgentVisibleRuntimeError(input: AgentVisibleRuntimeErrorInput): string {
  const lines = [
    `Runtime error code: ${input.code}.`,
    `Error: ${compactRuntimeErrorLine(input.message)}.`,
    ...(input.details ?? []).map((detail) => compactRuntimeErrorLine(detail)),
  ].filter((line): line is string => Boolean(line && line.length > 0));
  return lines.join("\n");
}

export function shouldContinueChildThreadAfterFailure(input: {
  recoverability: FailureRecoverability;
  scope: FailureScope;
}): boolean {
  if (input.recoverability === "retryable") return true;
  if (input.recoverability !== "repairable") return false;
  return input.scope !== "tool" && input.scope !== "approval";
}

export function buildChildThreadRecoveryPrompt(input: {
  message: string;
  recoverability: FailureRecoverability;
  scope: FailureScope;
}): string {
  return [
    "Continue the same assigned child task from the current thread and workspace state.",
    `Runtime error (${input.recoverability} ${describeFailureScope(input.scope)}): ${truncateForReasoning(input.message)}`,
    "Retry limit and loop guards remain active for this child task.",
  ].join("\n");
}

export function describeFailureScope(scope: FailureScope): string {
  switch (scope) {
    case "turn":
      return "turn";
    case "provider":
      return "provider";
    case "tool":
      return "tool";
    case "workflow":
      return "workflow";
    case "approval":
      return "approval";
    case "child-thread":
      return "child-thread";
    default:
      return "runtime";
  }
}

function compactRuntimeErrorLine(value: string): string {
  const compact = truncateText(normalizeWhitespace(value), 800);
  return compact.endsWith(".") ? compact.slice(0, -1) : compact;
}
