import type { ProviderTurnActionPolicy } from "@best-agent/protocol";
import type {
  ProviderDecisionOutput,
  ProviderSelectedToolCall,
  ProviderToolOption,
} from "../../provider/provider-core.js";
import type { RequiredToolConstraint } from "./tool-constraint-types.js";

export type ProviderDecisionValidation =
  | { ok: true }
  | {
      errorCode: string;
      invalidToolCall: ProviderSelectedToolCall;
      message: string;
      ok: false;
      reasonCode: "tool-not-allowed" | "tool-not-visible";
      source: "action-policy" | "profile" | "required-tool" | "visibility";
    };

export function validateProviderDecision(input: {
  actionPolicy: ProviderTurnActionPolicy;
  availableTools: ProviderToolOption[];
  decision: Extract<ProviderDecisionOutput, { kind: "tool" }>;
  knownToolNames: readonly string[];
  profileAllowedToolNames: readonly string[];
  profileName: string;
  requiredToolConstraint?: RequiredToolConstraint;
  selectedToolCalls: ProviderSelectedToolCall[];
  threadMode: "default" | "plan";
  visibleTools: ProviderToolOption[];
}): ProviderDecisionValidation {
  const disallowedKnownToolSelection =
    input.threadMode !== "plan"
      ? input.selectedToolCalls.find(
          (toolCall) =>
            input.knownToolNames.includes(toolCall.toolName) &&
            !input.profileAllowedToolNames.includes(toolCall.toolName),
        )
      : undefined;
  if (disallowedKnownToolSelection) {
    return {
      errorCode: "TOOL_NOT_ALLOWED",
      invalidToolCall: disallowedKnownToolSelection,
      message: `Tool ${disallowedKnownToolSelection.toolName} is not allowed by the ${input.profileName} profile.`,
      ok: false,
      reasonCode: "tool-not-allowed",
      source: "profile",
    };
  }

  const requiredToolConstraint = input.requiredToolConstraint;
  const forbiddenToolSelection = requiredToolConstraint
    ? input.selectedToolCalls.find(
        (toolCall) =>
          input.knownToolNames.includes(toolCall.toolName) &&
          requiredToolConstraint.forbiddenToolNames.includes(toolCall.toolName),
      )
    : undefined;
  if (requiredToolConstraint && forbiddenToolSelection) {
    return {
      errorCode: "TOOL_NOT_ALLOWED",
      invalidToolCall: forbiddenToolSelection,
      message: `Tool ${forbiddenToolSelection.toolName} is explicitly forbidden for this turn.`,
      ok: false,
      reasonCode: "tool-not-allowed",
      source: "required-tool",
    };
  }

  const unavailableToolSelection = input.selectedToolCalls.find(
    (toolCall) => !input.visibleTools.some((tool) => tool.name === toolCall.toolName),
  );
  if (unavailableToolSelection) {
    return {
      errorCode: "TOOL_NOT_ALLOWED",
      invalidToolCall: unavailableToolSelection,
      message: `Tool ${unavailableToolSelection.toolName} is not available for this turn.`,
      ok: false,
      reasonCode: "tool-not-visible",
      source: "visibility",
    };
  }

  return { ok: true };
}
