import type { PersistedProviderDecideCheckpointRequest } from "../checkpoint-codec.js";
import { classifyRuntimeFailure } from "../failure-classification.js";
import { promptDeliverableMissingObservation } from "./deliverable-observation.js";
import {
  collectReplayArtifactObligationFailures,
  compileReplayArtifactObligations,
} from "./provider-replay-deliverable-repair.js";
import { decideReplayRepairWithObservation } from "./provider-replay-repair-decision-runner.js";
import type { ReplayTerminalRepairState } from "./provider-replay-terminal-repair-state.js";
import { buildTerminalOutputInvalidRepair } from "./provider-terminal-output-handler.js";

export async function repairReplayDecideDeliverables(input: {
  responseText: string;
  state: ReplayTerminalRepairState;
}): Promise<string | undefined> {
  const { payload, replayContextAvailableTools } = input.state;
  const { runtime, turn } = input.state;
  if (payload.requestKind !== "decide" || !payload.request) {
    return undefined;
  }
  const request = runtime.hydrateProviderDecideCheckpointRequest(
    payload.request as PersistedProviderDecideCheckpointRequest,
  );
  const availableTools = replayContextAvailableTools ?? request.tools;
  const artifactObligations = compileReplayArtifactObligations({
    availableTools,
    cwd: runtime.cwd,
    messages: request.messages,
    objective: payload.replayContext?.objective,
    payloadArtifactObligations: payload.artifactObligations,
  });
  if (artifactObligations.length === 0) {
    return input.responseText;
  }
  const deliverableFailures = await collectReplayArtifactObligationFailures({
    artifactObligations,
    cwd: runtime.cwd,
    runtime,
    turn,
  });
  if (deliverableFailures.length === 0) {
    return input.responseText;
  }
  let responseText = await decideReplayRepairWithObservation({
    artifactObligations,
    missingProfileMessage:
      "Provider request checkpoint is missing the profile needed to repair replayed deliverables.",
    repairCode: "PROMPT_DELIVERABLE_MISSING",
    repairInput: {
      assertTerminal: false,
      repairActionPolicy: {
        allowedActions: ["tool"],
        directResponseAllowed: false,
      },
      requiresDeliverableAssertion: true,
      suppressStructuredControls: true,
    },
    repairObservation: promptDeliverableMissingObservation(deliverableFailures),
    repairSystemPrompt: request.systemPrompt,
    request,
    state: input.state,
  });
  try {
    return runtime.assertValidTerminalAssistantText(responseText);
  } catch (error) {
    const failure = classifyRuntimeFailure(error);
    if (failure.errorCode !== "TERMINAL_OUTPUT_INVALID") {
      throw error;
    }
    const terminalRepair = buildTerminalOutputInvalidRepair({
      baseActionPolicy: {
        allowedActions: ["tool"],
        directResponseAllowed: false,
      },
      deliverableFailures,
      observationToolNames: request.tools.map((tool) => tool.name),
      text: responseText,
      visibleTools: request.tools,
    });
    responseText = await decideReplayRepairWithObservation({
      artifactObligations,
      missingProfileMessage:
        "Provider request checkpoint is missing the profile needed to repair replayed deliverables.",
      repairCode: "TERMINAL_OUTPUT_INVALID",
      repairInput: {
        assertTerminal: true,
        repairActionPolicy: terminalRepair.actionPolicy,
        requiresDeliverableAssertion: terminalRepair.requiresDeliverableAssertion,
        suppressStructuredControls: terminalRepair.suppressStructuredControls,
      },
      repairObservation: terminalRepair.observation,
      repairSystemPrompt: request.systemPrompt,
      request,
      state: input.state,
    });
    await runtime.assertArtifactObligationsSatisfied(turn, artifactObligations);
    return responseText;
  }
}
