import type {
  ActionId,
  AskUserQuestion,
  CapabilityRequest,
  ItemId,
  SandboxProfile,
  ThreadEvidenceRecord,
  ThreadId,
  ToolInputValue,
  Turn,
  TurnId,
  WorkflowPhaseName,
} from "@best-agent/protocol";
import type { AgentProfile } from "../../agent/profile-registry.js";
import type { ThreadRuntimeSample } from "../../memory/thread-memory.js";
import type { RuntimePolicy } from "../../policy/runtime-policy.js";
import type {
  PreparedToolInvocation,
  ToolExecutionContext,
  ToolExecutionResultMetadata,
  ToolInputMode,
  ToolRegistry,
} from "../../tool/registry.js";
import type { ActionResolution, ToolResumeContext } from "../checkpoint-codec.js";
import type {
  CustomLoopReplayState,
  ExecuteToolInvocationOptions,
} from "../agent-runtime-tool-execution-types.js";

type RuntimeSampleStart = { cpu: NodeJS.CpuUsage; startedAt: bigint };

export type ToolAuthorizationResult = "allow" | import("@best-agent/protocol").RunTurnResponse;

export type DelegateTaskInput = {
  goal: string;
  profileName?: string;
  subagentType: WorkflowPhaseName;
};

export type ToolApprovalService = {
  authorizeCapability(
    turn: Turn,
    profile: AgentProfile,
    request: CapabilityRequest,
    toolCallId: ItemId,
    resumeCheckpoint: ExecuteToolInvocationOptions["resumeCheckpoint"] | undefined,
    resumeContext: ToolResumeContext | undefined,
    toolTurn: Turn,
  ): Promise<ToolAuthorizationResult>;
  recordActionResolvedControl(
    turn: Turn,
    actionId: ActionId,
    toolName: string,
    toolCallId: ItemId,
  ): Promise<void>;
  requestAction(
    turn: Turn,
    input: {
      questions: AskUserQuestion[];
      resumeContext?: ToolResumeContext;
      toolCallId: ItemId;
      toolName: string;
    },
  ): Promise<ActionResolution>;
};

export type ToolCapabilityService = {
  assertPlanModeToolAllowed(
    toolName: string,
    prepared: PreparedToolInvocation,
    threadId: ThreadId,
  ): Promise<void>;
  getDeterministicCapabilityDenial(
    profile: AgentProfile,
    request: CapabilityRequest,
  ): string | undefined;
  mergeSandboxProfiles(current: SandboxProfile | undefined, next: SandboxProfile): SandboxProfile;
  policyForProfile(profile: AgentProfile): RuntimePolicy;
};

export type ToolEvidenceService = {
  appendEvidenceRecords(records: ThreadEvidenceRecord[]): Promise<void>;
  buildToolEvidenceRecords(
    turn: Turn,
    toolName: string,
    input: ToolInputValue,
    output: string,
    toolResultItem: import("@best-agent/protocol").ToolResultItem,
    toolEnvelope: import("@best-agent/protocol").ToolResultEnvelope,
  ): Promise<ThreadEvidenceRecord[]>;
};

export type ToolDelegationService = {
  executeDelegateTaskBatchInvocation(
    turn: Turn,
    hostProfile: AgentProfile,
    activeSkillIds: string[],
    tasks: DelegateTaskInput[],
  ): Promise<string>;
  executeDelegateTaskInvocation(
    turn: Turn,
    hostProfile: AgentProfile,
    activeSkillIds: string[],
    task: DelegateTaskInput,
  ): Promise<string>;
  normalizeDelegateTasksInput(input: ToolInputValue): { tasks: DelegateTaskInput[] };
  serializeDelegateTasksInput(input: { tasks: DelegateTaskInput[] }): ToolInputValue;
};

export type ToolHookService = {
  applyPostToolUseHooks(request: {
    elapsedMs: number;
    profileName: string;
    result: string;
    threadId: ThreadId;
    toolCallId: ItemId;
    toolInput: ToolInputValue;
    toolName: string;
  }): Promise<{ output: string }>;
  applyPreToolUseHooks(request: {
    policyDecision: "allow";
    profileName: string;
    threadId: ThreadId;
    toolCallId: ItemId;
    toolInput: ToolInputValue;
    toolName: string;
  }): Promise<string | undefined>;
};

export type PreparedToolExecutor = {
  createToolContext(
    turn: Turn,
    input?: {
      abortSignal?: AbortSignal;
      heartbeat?: () => void;
      reportOutput?: (output: { stream: "stderr" | "stdout"; text: string }) => void;
      sandboxProfile?: SandboxProfile;
      setResultMetadata?: (metadata: ToolExecutionResultMetadata) => void;
      toolInputMode?: ToolInputMode;
      webSearchBudget?: { currentCount: number; limit: number; scope: "workflow" };
    },
  ): ToolExecutionContext;
  emitTurnToolOutputDelta(
    turn: Turn,
    callId: ItemId,
    toolName: string,
    stream: "stderr" | "stdout",
    delta: string,
  ): void;
  registerActiveToolAbortController(turnId: TurnId, controller: AbortController): () => void;
  tools: Pick<ToolRegistry, "executePrepared" | "getDefinition" | "prepare">;
};

export type ToolRuntimeSampleService = {
  captureRuntimeSampleStart(): RuntimeSampleStart;
  finishRuntimeSample(start: RuntimeSampleStart): ThreadRuntimeSample;
  memory: { recordToolExecution(threadId: ThreadId, sample: ThreadRuntimeSample): void };
};

export type WebSearchBudgetService = {
  reserveWebSearchBudget(
    turn: Turn,
    toolName: string,
  ): { currentCount: number; limit: number; scope: "workflow" } | undefined;
};

export type ToolExecutionService = ToolHookService &
  PreparedToolExecutor &
  ToolRuntimeSampleService &
  WebSearchBudgetService;

export type ToolFormattingService = {
  formatToolInputForPrompt(input: ToolInputValue): string;
  isContentBearingToolInputForCheckpoint(toolName: string, input: ToolInputValue): boolean;
  previewDebugText(value: string, maxLength: number): string;
  previewToolInputForDebugLog(input: ToolInputValue): unknown;
  renderToolInputForProviderHistory(toolName: string, input: ToolInputValue): ToolInputValue;
  stripChildPersistenceDirectives(reasoning: string): string;
};

export type ToolReplayService = {
  takeCustomLoopToolReplayResult(
    state: CustomLoopReplayState,
    toolName: string,
    toolInput: ToolInputValue,
  ): string | undefined;
};

export type ToolTraceService = {
  describeAuthorizationResultForDebugLog(result: ToolAuthorizationResult): unknown;
  describeCapabilityRequestForDebugLog(request: CapabilityRequest): Record<string, unknown>;
  describeErrorForDebugLog(error: unknown): { code?: string; message: string; name?: string };
  logShellToolDebug(cwd: string, eventName: string, payload?: unknown): void;
};
