import type {
  ArtifactObligation,
  ItemId,
  PhaseResultPhaseName,
  ProviderConfig,
  ProviderTurnActionPolicy,
  RunPhaseName,
  SessionUsageOperation,
  ThreadEvidenceRecord,
  ThreadId,
  ToolInputValue,
  ToolReplayPolicy,
  ToolResultItem,
  Turn,
  TurnId,
} from "@best-agent/protocol";
import type { AgentProfile } from "../../agent/profile-registry.js";
import type {
  ConversationMessage,
  ProviderCacheControl,
  ProviderDecisionOutput,
  ProviderTextStreamCallbacks,
  ProviderToolOption,
} from "../../provider/model-provider.js";
import type { PromptDeliverableFailure } from "../../prompt/runtime-prompts.js";
import type {
  CompletedProviderToolCall,
  PendingProviderToolCall,
  ProviderCorrectionTrace,
  ToolResumeContext,
} from "../checkpoint-codec.js";
import type { ProviderTurnInvokeContext } from "./provider-turn-budget.js";
import type { ProviderDecisionResolutionInput } from "./provider-decision-resolution.js";

export type ProviderToolLoopDecisionPreview = {
  callbacks: ProviderTextStreamCallbacks;
  finalize: () => Promise<void>;
  resolveToolCallId: (providerToolCallId?: string) => ItemId | undefined;
};

export type ProviderToolLoopRouteInput = {
  downgradeEligible?: boolean;
  escalationLevel?: number;
  fixedRouteConfigOverride?: boolean;
  operation: SessionUsageOperation;
  phaseName?: RunPhaseName;
  profileName: string;
  routeConfigOverride?: ProviderConfig;
  threadId: ThreadId;
  toolStepCount?: number;
  turnId: TurnId;
};

export type ProviderToolLoopApprovals = {
  resolveApprovalTurn: (turn: Turn, context: ToolResumeContext) => Promise<Turn>;
};

export type ProviderToolLoopCheckpoints = {
  resolveReplayToolInput: (call: PendingProviderToolCall) => Promise<ToolInputValue>;
  restoreToolOutputCheckpointArtifacts: (
    turn: Turn,
    context: Pick<ToolResumeContext, "toolCallId">,
    toolResultItem?: ToolResultItem,
    evidenceRecords?: ThreadEvidenceRecord[],
    input?: { persistToolResult?: boolean },
  ) => Promise<ToolResultItem | undefined>;
};

export type ProviderToolLoopExecution = {
  executeToolInvocation: (input: {
    activeSkillIds: string[];
    approvalTurn: Turn;
    profile: AgentProfile;
    reasoning?: string;
    resumeContext: ToolResumeContext;
    toolCallId: ItemId;
    toolInput: ToolInputValue;
    toolName: string;
    turn: Turn;
  }) => Promise<string>;
};

export type ProviderToolLoopInvoker = {
  createCorrectionTrace: (input: {
    code: ProviderCorrectionTrace["code"];
    error?: ProviderCorrectionTrace["error"];
    messages: ConversationMessage[];
    operation: SessionUsageOperation;
    phaseResultPhaseName?: PhaseResultPhaseName;
    systemPrompt?: string;
    tools?: ProviderToolOption[];
  }) => ProviderCorrectionTrace;
  createDecisionStreamPreview: (
    turn: Turn,
    allowTextPreview: boolean,
    leakToolNames: readonly string[],
    noActionTimeoutMs?: number,
  ) => ProviderToolLoopDecisionPreview;
  decide: (
    threadId: ThreadId,
    turnId: TurnId,
    input: {
      actionPolicy?: ProviderTurnActionPolicy;
      allowPhaseResult?: boolean;
      allowPlanUpdate?: boolean;
      cacheControl?: ProviderCacheControl;
      messages: ConversationMessage[];
      phaseResultPhaseName?: PhaseResultPhaseName;
      stream?: ProviderTextStreamCallbacks;
      systemPrompt?: string;
      tools: ProviderToolOption[];
    },
    operation: SessionUsageOperation,
    context: ProviderTurnInvokeContext,
  ) => Promise<ProviderDecisionOutput>;
  decisionNoActionTimeoutMs: () => number;
  generate: (
    threadId: ThreadId,
    turnId: TurnId,
    input: {
      cacheControl?: ProviderCacheControl;
      messages: ConversationMessage[];
      systemPrompt?: string;
    },
    operation: SessionUsageOperation,
    context: ProviderTurnInvokeContext,
  ) => Promise<{ text: string }>;
  generateUnbudgetedFinalization: (
    threadId: ThreadId,
    turnId: TurnId,
    input: {
      cacheControl?: ProviderCacheControl;
      messages: ConversationMessage[];
      systemPrompt?: string;
    },
    operation: SessionUsageOperation,
    context: ProviderTurnInvokeContext,
  ) => Promise<{ text: string }>;
};

export type ProviderToolLoopResponses = {
  finalizeGeneratedResponse: (input: {
    availableTools: readonly ProviderToolOption[];
    messages: ConversationMessage[];
    objective?: string;
    text: string;
  }) => Promise<string>;
};

export type ProviderToolLoopObservations = {
  collectArtifactObligationFailuresForTurn: (
    turn: Turn,
    obligations: ArtifactObligation[],
  ) => Promise<PromptDeliverableFailure[]>;
  cwd: string;
  renderToolInputForProviderHistory: (toolName: string, input: ToolInputValue) => ToolInputValue;
  takePendingToolHookContextPrompt: (threadId: ThreadId) => string | undefined;
  toolReplayPolicyForFollowUp: (
    toolName: string,
  ) => ToolReplayPolicy | CompletedProviderToolCall["replayPolicy"] | undefined;
};

export type ProviderToolLoopProfiles = {
  resolveProfile: (profileName: string) => AgentProfile;
};

export type ProviderToolLoopRouting = {
  advancedPhaseReopenCount: (turnId: TurnId) => Promise<number>;
  resolveProviderRoute: (input: ProviderToolLoopRouteInput) => { config: ProviderConfig };
};

export type ProviderToolLoopRuntime = {
  approvals: ProviderToolLoopApprovals;
  checkpoints: ProviderToolLoopCheckpoints;
  execution: ProviderToolLoopExecution;
  observations: ProviderToolLoopObservations;
  profiles: ProviderToolLoopProfiles;
  provider: ProviderToolLoopInvoker;
  resolveProviderDecision: (input: ProviderDecisionResolutionInput) => Promise<string>;
  responses: ProviderToolLoopResponses;
  routing: ProviderToolLoopRouting;
};
