import type {
  ArtifactObligation,
  ProviderConfig,
  ProviderTurnActionPolicy,
} from "@best-agent/protocol";
import type { AgentProfile } from "../../agent/profile-registry.js";
import type { ProviderToolOption } from "../../provider/model-provider.js";
import type {
  ProviderCorrectionTrace,
  ProviderDecideCheckpointRequest,
  ProviderRequestCheckpointPayload,
} from "../checkpoint-codec.js";
import { runReplayRepairDecision } from "./provider-replay-decision-runner.js";
import { requireReplayRepairProfile } from "./provider-replay-profile.js";
import { isRoutingDowngradeEligible } from "./provider-replay-context.js";
import type { ReplayTerminalRepairState } from "./provider-replay-terminal-repair-state.js";
import type { ProviderTurnBudget } from "./provider-turn-budget.js";
import { appendRuntimeObservation, type RuntimeObservation } from "./runtime-observation.js";
import { resolveProviderVisibleTools } from "./tool-constraint.js";
import type { RequiredToolConstraint } from "./tool-constraint-types.js";

export async function decideReplayRepairWithObservation(input: {
  artifactObligations: ArtifactObligation[];
  missingProfileMessage: string;
  repairCode: ProviderCorrectionTrace["code"];
  repairInput: {
    assertTerminal?: boolean;
    repairActionPolicy: ProviderTurnActionPolicy;
    requiresDeliverableAssertion?: boolean;
    suppressStructuredControls?: boolean;
  };
  repairObservation: RuntimeObservation;
  repairSystemPrompt: string | undefined;
  request: ProviderDecideCheckpointRequest;
  state: ReplayTerminalRepairState;
}): Promise<string> {
  const { payload, replayContextAvailableTools, replayProviderTurnBudget, replayRouteConfig } =
    input.state;
  const { runtime, turn } = input.state;
  const requiresDeliverableAssertion =
    input.repairInput.requiresDeliverableAssertion ||
    input.repairCode === "PROMPT_DELIVERABLE_MISSING";
  const suppressStructuredControls =
    input.repairInput.suppressStructuredControls || requiresDeliverableAssertion;
  const availableTools = replayContextAvailableTools ?? input.request.tools;
  const repairMessages = appendRuntimeObservation(input.request.messages, input.repairObservation);
  const visibleTools = suppressStructuredControls
    ? resolveProviderVisibleTools(availableTools, undefined, repairMessages)
    : input.request.tools;
  const requiredToolConstraint = suppressStructuredControls
    ? undefined
    : payload.replayContext?.requiredToolConstraint;
  const allowPhaseResult =
    !suppressStructuredControls &&
    (input.request.allowPhaseResult ?? false) &&
    Boolean(input.request.phaseResultPhaseName) &&
    input.repairInput.repairActionPolicy.allowedActions.includes("phase_result");
  const allowPlanUpdate =
    !suppressStructuredControls &&
    (input.request.allowPlanUpdate ?? false) &&
    input.repairInput.repairActionPolicy.allowedActions.includes("plan_update");
  const { profile, profileName } = requireReplayRepairProfile({
    missingProfileMessage: input.missingProfileMessage,
    profileName: payload.replayContext?.profileName,
    runtime,
  });
  return runReplayRepairDecision({
    actionPolicy: input.repairInput.repairActionPolicy,
    allowPhaseResult,
    allowPlanUpdate,
    artifactObligations: input.artifactObligations,
    assertArtifacts: requiresDeliverableAssertion,
    assertTerminal: input.repairInput.assertTerminal,
    availableTools,
    baseMessages: input.request.messages,
    cacheControl: input.request.cacheControl,
    controls: {
      allowPhaseResult,
      allowPlanUpdate,
      ...(allowPhaseResult && input.request.phaseResultPhaseName
        ? { phaseResultPhaseName: input.request.phaseResultPhaseName }
        : {}),
    },
    correctionOperation: payload.operation,
    phaseResultPhaseName: allowPhaseResult ? input.request.phaseResultPhaseName : undefined,
    providerContext: replayTurnContext({
      artifactObligations: input.artifactObligations,
      availableTools,
      payload,
      profile,
      profileName,
      replayProviderTurnBudget,
      replayRouteConfig,
      requiredToolConstraint,
    }),
    repairCode: input.repairCode,
    repairObservation: input.repairObservation,
    requiredToolConstraint,
    resolveContext: replayTurnContext({
      artifactObligations: input.artifactObligations,
      availableTools,
      downgradeEligible: isRoutingDowngradeEligible(payload.replayContext?.phaseName),
      payload,
      profile,
      profileName,
      replayProviderTurnBudget,
      replayRouteConfig,
      requiredToolConstraint,
    }),
    runtime,
    objective: payload.replayContext?.objective,
    systemPrompt: input.repairSystemPrompt,
    toolsForTrace: input.request.tools,
    toolStepCount: payload.replayContext?.toolStepCount ?? 0,
    traceSystemPrompt: input.request.systemPrompt,
    turn,
    visibleTools,
  });
}

export function replayTurnContext(input: {
  artifactObligations?: ArtifactObligation[];
  availableTools?: ProviderToolOption[];
  downgradeEligible?: boolean;
  payload: ProviderRequestCheckpointPayload;
  profile: AgentProfile;
  profileName: string;
  replayProviderTurnBudget: ProviderTurnBudget;
  replayRouteConfig?: ProviderConfig;
  requiredToolConstraint?: RequiredToolConstraint;
}) {
  const replayContext = input.payload.replayContext;
  return {
    activeSkillIds: replayContext?.activeSkillIds ?? [],
    advancedPhase: replayContext?.advancedPhase,
    artifactObligations: input.artifactObligations ?? input.payload.artifactObligations,
    availableTools: input.availableTools,
    budget: input.replayProviderTurnBudget,
    compactionFollowUpMarker: replayContext?.compactionFollowUpMarker,
    downgradeEligible: input.downgradeEligible ?? replayContext?.downgradeEligible,
    escalationLevel: replayContext?.escalationLevel,
    fixedRouteConfigOverride: Boolean(input.replayRouteConfig),
    objective: replayContext?.objective,
    phaseName: replayContext?.phaseName,
    profile: input.profile,
    profileName: input.profileName,
    requiredToolConstraint: input.requiredToolConstraint,
    routeConfigOverride: input.replayRouteConfig,
    requestBudgetSnapshot: undefined,
    supersedePendingProviderRequest: true,
    toolStepCount: replayContext?.toolStepCount ?? 0,
  };
}
