module GraphQL::Client::QueryTypename

def self.insert_typename_fields(document, types: {})

def self.insert_typename_fields(document, types: {})
  visitor = InsertTypenameVisitor.new(document, types: types)
  visitor.visit
  visitor.result
end

def self.insert_typename_fields(document, types: {})

def self.insert_typename_fields(document, types: {})
  on_selections = ->(node, _parent) do
    type = types[node]
    if node.selections.any?
      case type && type.unwrap
      when NilClass, GraphQL::InterfaceType, GraphQL::UnionType
        names = node_flatten_selections(node.selections).map { |s| s.respond_to?(:name) ? s.name : nil }
        names = Set.new(names.compact)
        unless names.include?("__typename")
          node.selections = [GraphQL::Language::Nodes::Field.new(name: "__typename")] + node.selections
        end
      end
    elsif type && type.unwrap.is_a?(GraphQL::ObjectType)
      node.selections = [GraphQL::Language::Nodes::Field.new(name: "__typename")]
    end
  end
  visitor = GraphQL::Language::Visitor.new(document)
  visitor[GraphQL::Language::Nodes::Field].leave << on_selections
  visitor[GraphQL::Language::Nodes::FragmentDefinition].leave << on_selections
  visitor[GraphQL::Language::Nodes::OperationDefinition].leave << on_selections
  visitor.visit
  document
end

def self.node_flatten_selections(selections)

def self.node_flatten_selections(selections)
  selections.flat_map do |selection|
    case selection
    when GraphQL::Language::Nodes::Field
      selection
    when GraphQL::Language::Nodes::InlineFragment
      node_flatten_selections(selection.selections)
    else
      []
    end
  end
end