import { BestAgentError } from "@best-agent/foundation";
import type { ProviderDecisionOutput } from "../../provider/model-provider.js";
import type { ProviderTurnActionPolicy } from "@best-agent/protocol";
import { splitRequiredToolGroups, type RequiredToolConstraint } from "./tool-constraint-types.js";

export function assertValidProviderTurnActionPolicy(policy: ProviderTurnActionPolicy): void {
  if (policy.allowedActions.length === 0) {
    throw new BestAgentError(
      "PROVIDER_DECISION_INVALID",
      "Provider turn action policy must allow at least one action.",
    );
  }
  if (policy.directResponseAllowed && !policy.allowedActions.includes("respond")) {
    throw new BestAgentError(
      "PROVIDER_DECISION_INVALID",
      'Provider turn action policy cannot allow direct responses without "respond".',
    );
  }
  if (policy.required?.kind === "tool" && !policy.allowedActions.includes("tool")) {
    throw new BestAgentError(
      "PROVIDER_DECISION_INVALID",
      'Provider turn action policy cannot require a tool without allowing "tool".',
    );
  }
  if (policy.required?.kind === "plan_update" && !policy.allowedActions.includes("plan_update")) {
    throw new BestAgentError(
      "PROVIDER_DECISION_INVALID",
      'Provider turn action policy cannot require a plan update without allowing "plan_update".',
    );
  }
  if (policy.required?.kind === "phase_result" && !policy.allowedActions.includes("phase_result")) {
    throw new BestAgentError(
      "PROVIDER_DECISION_INVALID",
      'Provider turn action policy cannot require a phase result without allowing "phase_result".',
    );
  }
}

export function assertDecisionAllowedByActionPolicy(
  decision: ProviderDecisionOutput,
  policy: ProviderTurnActionPolicy,
): void {
  assertValidProviderTurnActionPolicy(policy);
  if (decision.kind === "repairable_invalid") {
    throw new BestAgentError("PROVIDER_DECISION_INVALID", decision.message);
  }
  if (!policy.allowedActions.includes(decision.kind)) {
    throw new BestAgentError(
      "PROVIDER_DECISION_INVALID",
      `Provider returned "${decision.kind}", but this turn only allows: ${policy.allowedActions.join(", ")}.`,
    );
  }
  if (decision.kind === "respond" && !policy.directResponseAllowed) {
    throw new BestAgentError(
      "PROVIDER_DECISION_INVALID",
      "Provider returned a direct response even though this turn requires a structured action.",
    );
  }
}

export function actionPolicyRequiredToolNames(policy: ProviderTurnActionPolicy): string[] {
  if (policy.required?.kind !== "tool") {
    return [];
  }
  const names = [
    ...(policy.required.toolNames ?? []),
    ...(policy.required.requiredAllToolNames ?? []),
    ...(policy.required.requiredAnyToolNames ?? []),
    ...(policy.required.requiredAnyToolGroups ?? []).flat(),
  ];
  return names.filter((name, index) => name && names.indexOf(name) === index);
}

export function actionPolicyAllowsToolSelection(
  policy: ProviderTurnActionPolicy,
  _toolName: string,
): boolean {
  return policy.allowedActions.includes("tool");
}

export function buildActionPolicyWrongToolSelectionError(
  _policy: ProviderTurnActionPolicy,
  toolName: string,
): string {
  return `Tool ${toolName} is not allowed by the current runtime action policy.`;
}

export function constraintHasPendingRequiredTools(
  constraint: RequiredToolConstraint | undefined,
): constraint is RequiredToolConstraint {
  if (!constraint) {
    return false;
  }
  const groups = splitRequiredToolGroups(constraint);
  return groups.requiredAllToolNames.length > 0 || groups.requiredAnyToolGroups.length > 0;
}
