export type RequiredToolConstraint = {
  conflicts?: ToolPolicyConflictEvidence[];
  evidence?: ToolInstructionEvidence[];
  forbiddenToolNames: string[];
  primaryToolName?: string;
  reason:
    | "explicit-exclusive-tool-set"
    | "explicit-forbidden-tool"
    | "explicit-required-tool"
    | "repo-wide-coverage"
    | "requested-subagent";
  requiredAnyToolNames?: string[];
  requiredAnyToolGroups?: string[][];
  requiredAllToolNames?: string[];
  requiredReleasePolicy: "after-required-tools-succeeded" | "none";
  requiredToolNames: string[];
  restrictionScope: "turn" | "until-required-satisfied";
  selectableToolNames?: string[];
  visibleToolNames?: string[];
};

export type ToolPolicyConflictEvidence = {
  evidence: ToolInstructionEvidence[];
  kind:
    | "allow-only-conflict"
    | "allow-only-forbidden"
    | "required-forbidden"
    | "required-not-allowed";
  toolNames: string[];
};

export type ToolInstructionEvidence = {
  end: number;
  parser: string;
  start: number;
  text: string;
};

export function requiredAnyToolGroupsForConstraint(constraint: RequiredToolConstraint): string[][] {
  const groups = [
    ...(constraint.requiredAnyToolGroups ?? []),
    ...(constraint.requiredAnyToolNames ? [constraint.requiredAnyToolNames] : []),
  ];
  const normalizedGroups: string[][] = [];

  for (const group of groups) {
    const normalizedGroup = [...new Set(group)].filter((toolName) => toolName.length > 0);
    if (
      normalizedGroup.length > 1 &&
      !normalizedGroups.some(
        (existingGroup) =>
          existingGroup.length === normalizedGroup.length &&
          existingGroup.every((toolName, index) => toolName === normalizedGroup[index]),
      )
    ) {
      normalizedGroups.push(normalizedGroup);
    }
  }

  return normalizedGroups;
}

export function splitRequiredToolGroups(constraint: RequiredToolConstraint): {
  requiredAllToolNames: string[];
  requiredAnyToolGroups: string[][];
  requiredAnyToolNames: string[];
} {
  const requiredAnyToolGroups = requiredAnyToolGroupsForConstraint(constraint);
  const requiredAnyToolNames = [...new Set(requiredAnyToolGroups.flat())];

  if (constraint.requiredAllToolNames) {
    return {
      requiredAllToolNames: constraint.requiredAllToolNames,
      requiredAnyToolGroups,
      requiredAnyToolNames,
    };
  }

  return {
    // Legacy fallback for persisted constraints that predate requiredAllToolNames.
    requiredAllToolNames: constraint.requiredToolNames.filter(
      (toolName) => !requiredAnyToolNames.includes(toolName),
    ),
    requiredAnyToolGroups,
    requiredAnyToolNames,
  };
}
