import type { ProviderTurnActionPolicy } from "@best-agent/protocol";
import type { ProviderToolOption } from "../../provider/provider-core.js";
import type { PromptDeliverableFailure } from "../../prompt/runtime-prompts.js";
import {
  terminalOutputInvalidObservation,
  type RuntimeObservation,
  type TerminalOutputInvalidKind,
} from "./runtime-observation.js";
import {
  classifyTerminalOutputInvalid,
  type TerminalOutputInvalidClassification,
} from "./terminal-output-classifier.js";
import { assertValidProviderTurnActionPolicy } from "./provider-action-policy.js";

export function terminalOutputInvalidActionPolicy(input: {
  baseActionPolicy: ProviderTurnActionPolicy;
  deliverableFailures: PromptDeliverableFailure[];
  invalidKind?: TerminalOutputInvalidKind;
  leakedToolKnown?: boolean;
  visibleTools: ProviderToolOption[];
}): ProviderTurnActionPolicy {
  if (input.deliverableFailures.length > 0) {
    return intersectRepairPolicy(input.baseActionPolicy, {
      allowedActions: ["tool"],
      directResponseAllowed: false,
    });
  }

  const policy = {
    allowedActions: input.baseActionPolicy.allowedActions,
    directResponseAllowed:
      input.baseActionPolicy.directResponseAllowed &&
      input.baseActionPolicy.allowedActions.includes("respond"),
    required:
      input.baseActionPolicy.required?.kind === "tool"
        ? input.baseActionPolicy.required
        : undefined,
  };
  assertValidProviderTurnActionPolicy(policy);
  return policy;
}

export function buildTerminalOutputInvalidRepair(input: {
  baseActionPolicy: ProviderTurnActionPolicy;
  classificationToolNames?: readonly string[];
  deliverableFailures: PromptDeliverableFailure[];
  observationToolNames?: readonly string[];
  text: string;
  visibleTools: ProviderToolOption[];
}): {
  actionPolicy: ProviderTurnActionPolicy;
  classification: TerminalOutputInvalidClassification;
  observation: RuntimeObservation;
  requiresDeliverableAssertion: boolean;
  requiresNativeToolOnlyRepair: boolean;
  suppressStructuredControls: boolean;
} {
  const visibleToolNames = input.visibleTools.map((tool) => tool.name);
  const classification = classifyTerminalOutputInvalid(
    input.text,
    input.classificationToolNames ?? visibleToolNames,
  );
  const actionPolicy = terminalOutputInvalidActionPolicy({
    baseActionPolicy: input.baseActionPolicy,
    deliverableFailures: input.deliverableFailures,
    invalidKind: classification.invalidKind,
    leakedToolKnown: classification.toolKnown,
    visibleTools: input.visibleTools,
  });
  const requiresNativeToolOnlyRepair =
    actionPolicy.allowedActions.length === 1 &&
    actionPolicy.allowedActions[0] === "tool" &&
    actionPolicy.directResponseAllowed === false;
  return {
    actionPolicy,
    classification,
    observation: terminalOutputInvalidObservation({
      actionPolicy,
      deliverableFailures: input.deliverableFailures,
      invalidKind: classification.invalidKind,
      leakedToolKnown: classification.toolKnown,
      leakedToolName: classification.toolName,
      reasonCode: classification.reasonCode,
      ...(input.observationToolNames ? { toolNames: input.observationToolNames } : {}),
    }),
    requiresDeliverableAssertion: input.deliverableFailures.length > 0,
    requiresNativeToolOnlyRepair,
    suppressStructuredControls: requiresNativeToolOnlyRepair,
  };
}

function intersectRepairPolicy(
  base: ProviderTurnActionPolicy,
  proposed: ProviderTurnActionPolicy,
): ProviderTurnActionPolicy {
  const allowedActions = proposed.allowedActions.filter((action) =>
    base.allowedActions.includes(action),
  );
  const required = proposed.required ?? base.required;
  if (allowedActions.length === 0) {
    const policy = {
      allowedActions: base.allowedActions,
      directResponseAllowed: base.directResponseAllowed && base.allowedActions.includes("respond"),
      required: base.required,
    };
    assertValidProviderTurnActionPolicy(policy);
    return policy;
  }
  const policy = {
    allowedActions,
    directResponseAllowed:
      base.directResponseAllowed &&
      proposed.directResponseAllowed &&
      allowedActions.includes("respond"),
    required: required && allowedActions.includes(required.kind) ? required : undefined,
  };
  assertValidProviderTurnActionPolicy(policy);
  return policy;
}
