import type {
  ArtifactObligation,
  Item,
  ProviderConfig,
  ProviderDecisionTransport,
  ProviderKind,
  ProviderTurnActionPolicy,
  RunPhaseName,
  SessionUsageOperation,
  ThreadEvidenceRecord,
  ThreadId,
  ToolInputValue,
  ToolReplayPolicy,
  Turn,
  TurnId,
} from "@best-agent/protocol";
import type { AgentProfile } from "../../agent/profile-registry.js";
import type {
  ConversationMessage,
  ProviderDecisionOutput,
  ProviderToolOption,
} from "../../provider/model-provider.js";
import type { PromptDeliverableFailure } from "../../prompt/runtime-prompts.js";
import type {
  PersistedProviderDecideCheckpointRequest,
  ProviderCorrectionTrace,
  ProviderDecideCheckpointRequest,
  ProviderGenerateCheckpointRequest,
  ProviderRequestCheckpointPayload,
} from "../checkpoint-codec.js";
import type { ProviderDecisionResolutionInput } from "./provider-decision-resolution.js";
import type { ProviderTurnInvokeContext } from "./provider-turn-budget.js";
import type { RequiredToolConstraint } from "./tool-constraint-types.js";

export type ProviderReplayRepairRouteContext = {
  activeSkillIds?: string[];
  advancedPhase?: ProviderTurnInvokeContext["advancedPhase"];
  artifactObligations?: ArtifactObligation[];
  availableTools?: ProviderToolOption[];
  compactionFollowUpMarker?: ProviderTurnInvokeContext["compactionFollowUpMarker"];
  downgradeEligible?: boolean;
  escalationLevel?: number;
  fixedRouteConfigOverride?: boolean;
  objective?: string;
  operation: SessionUsageOperation;
  payloadDecisionTransport?: ProviderDecisionTransport;
  payloadModel?: string;
  payloadProviderKind?: ProviderKind;
  phaseName?: RunPhaseName;
  profileName?: string;
  providerTurnBudgeted?: boolean;
  providerTurnCount?: number;
  correctionTrace?: ProviderCorrectionTrace;
  requiredToolConstraint?: RequiredToolConstraint;
  routeConfigOverride?: ProviderConfig;
  requestBudgetSnapshot?: ProviderTurnInvokeContext["requestBudgetSnapshot"];
  supersedePendingProviderRequest?: boolean;
  threadId: ThreadId;
  toolStepCount?: number;
  turnId?: TurnId;
};

type ReplayProvider = {
  decide: (
    threadId: ThreadId,
    turnId: TurnId,
    request: {
      actionPolicy?: ProviderTurnActionPolicy;
      allowPhaseResult?: boolean;
      allowPlanUpdate?: boolean;
      cacheControl?: ProviderDecisionResolutionInput["cacheControl"];
      messages: ConversationMessage[];
      phaseResultPhaseName?: ProviderDecisionResolutionInput["controls"]["phaseResultPhaseName"];
      systemPrompt?: string;
      tools: ProviderToolOption[];
    },
    operation: SessionUsageOperation,
    context: ProviderTurnInvokeContext,
  ) => Promise<ProviderDecisionOutput>;
  generate: (
    threadId: ThreadId,
    turnId: TurnId,
    request: ProviderGenerateCheckpointRequest,
    operation: SessionUsageOperation,
    context: ProviderTurnInvokeContext,
  ) => Promise<{ text: string }>;
};

export type ProviderReplayRepairRuntime = {
  advancedPhaseReopenCount: (turnId: TurnId) => Promise<number>;
  assertArtifactObligationsSatisfied: (
    turn: Turn,
    obligations: ArtifactObligation[],
  ) => Promise<void>;
  assertValidTerminalAssistantText: (text: string) => string;
  assertValidTerminalAssistantTextOrFallback: (turn: Turn, text: string) => Promise<string>;
  buildCorrectionTrace: (input: {
    code: ProviderCorrectionTrace["code"];
    messages: ConversationMessage[];
    operation: SessionUsageOperation;
    phaseResultPhaseName?: ProviderDecisionResolutionInput["controls"]["phaseResultPhaseName"];
    systemPrompt?: string;
    tools?: ProviderToolOption[];
  }) => ProviderCorrectionTrace;
  collectArtifactObligationFailuresForTurn: (
    turn: Turn,
    obligations: ArtifactObligation[],
  ) => Promise<PromptDeliverableFailure[]>;
  cwd: string;
  defaultMaxProviderTurns: number;
  evidenceList: (input: { threadId: ThreadId; turnId: TurnId }) => Promise<ThreadEvidenceRecord[]>;
  finalizeGeneratedResponse: (input: {
    availableTools: readonly ProviderToolOption[];
    messages: ConversationMessage[];
    objective?: string;
    text: string;
  }) => Promise<string>;
  hydrateProviderDecideCheckpointRequest: (
    request: PersistedProviderDecideCheckpointRequest,
  ) => ProviderDecideCheckpointRequest;
  hydrateProviderReplayContextAvailableTools: (
    payload: Pick<ProviderRequestCheckpointPayload, "replayContext">,
  ) => ProviderToolOption[] | undefined;
  hydrateProviderRequestReplayConfig: (
    payload: Pick<ProviderRequestCheckpointPayload, "providerConfig" | "replayContext">,
  ) => ProviderConfig | undefined;
  listItems: (threadId: ThreadId) => Promise<Item[]>;
  maxProviderTurnsForProfile: (profile: AgentProfile) => number;
  provider: ReplayProvider;
  renderToolInputForProviderHistory: (toolName: string, input: ToolInputValue) => ToolInputValue;
  requireProfile: (profileName: string) => AgentProfile;
  resolveProviderDecision: (input: ProviderDecisionResolutionInput) => Promise<string>;
  resolveProviderRoute: (input: ProviderReplayRepairRouteContext) => { config: ProviderConfig };
  resolveReplayPolicyForFollowUp: (toolName: string) => ToolReplayPolicy | undefined;
};

export function isRoutingDowngradeEligible(phaseName?: RunPhaseName): boolean {
  return (
    phaseName === undefined ||
    phaseName === "inspect" ||
    phaseName === "summarize" ||
    phaseName === "wait"
  );
}
