module Tapioca::Dsl::Helpers::GraphqlTypeHelper

def has_replaceable_default?(argument)

: (GraphQL::Schema::Argument argument) -> bool
def has_replaceable_default?(argument)
  !!argument.replace_null_with_default? && !argument.default_value.nil?
end

def type_for(type, ignore_nilable_wrapper: false, prepare_method: nil)

: ((GraphQL::Schema::Wrapper | singleton(GraphQL::Schema::Scalar) | singleton(GraphQL::Schema::Enum) | singleton(GraphQL::Schema::Union) | singleton(GraphQL::Schema::Object) | singleton(GraphQL::Schema::Interface) | singleton(GraphQL::Schema::InputObject)) type, ?ignore_nilable_wrapper: bool, ?prepare_method: Method?) -> String
def type_for(type, ignore_nilable_wrapper: false, prepare_method: nil)
  unwrapped_type = type.unwrap
  parsed_type = case unwrapped_type
  when GraphQL::Types::Boolean.singleton_class
    "T::Boolean"
  when GraphQL::Types::Float.singleton_class
    type_for_constant(Float)
  when GraphQL::Types::ID.singleton_class, GraphQL::Types::String.singleton_class
    type_for_constant(String)
  when GraphQL::Types::Int.singleton_class, GraphQL::Types::BigInt.singleton_class
    type_for_constant(Integer)
  when GraphQL::Types::ISO8601Date.singleton_class
    type_for_constant(Date)
  when GraphQL::Types::ISO8601DateTime.singleton_class
    type_for_constant(Time)
  when GraphQL::Types::JSON.singleton_class
    "T::Hash[::String, T.untyped]"
  when GraphQL::Schema::Enum.singleton_class
    enum_values = T.cast(unwrapped_type.enum_values, T::Array[GraphQL::Schema::EnumValue])
    value_types = enum_values.map { |v| type_for_constant(v.value.class) }.uniq
    if value_types.size == 1
      T.must(value_types.first)
    else
      "T.any(#{value_types.join(", ")})"
    end
  when GraphQL::Schema::Scalar.singleton_class
    method = Runtime::Reflection.method_of(unwrapped_type, :coerce_input)
    signature = Runtime::Reflection.signature_of(method)
    return_type = signature&.return_type
    valid_return_type?(return_type) ? return_type.to_s : "T.untyped"
  when GraphQL::Schema::InputObject.singleton_class
    type_for_constant(unwrapped_type)
  when Module
    Runtime::Reflection.qualified_name_of(unwrapped_type) || "T.untyped"
  else
    "T.untyped"
  end
  if prepare_method
    prepare_signature = Runtime::Reflection.signature_of(prepare_method)
    prepare_return_type = prepare_signature&.return_type
    if valid_return_type?(prepare_return_type)
      parsed_type = prepare_return_type&.to_s
    end
  end
  if type.list?
    parsed_type = "T::Array[#{parsed_type}]"
  end
  unless type.non_null? || ignore_nilable_wrapper
    parsed_type = RBIHelper.as_nilable_type(parsed_type)
  end
  parsed_type
end

def type_for_argument(argument, constant)

: (GraphQL::Schema::Argument argument, (singleton(GraphQL::Schema::Mutation) | singleton(GraphQL::Schema::InputObject)) constant) -> String
def type_for_argument(argument, constant)
  type = if argument.loads
    loads_type = ::GraphQL::Schema::Wrapper.new(argument.loads)
    loads_type = loads_type.to_list_type if argument.type.list?
    loads_type = loads_type.to_non_null_type if argument.type.non_null?
    loads_type
  else
    argument.type
  end
  prepare = argument.prepare
  prepare_method = if prepare.is_a?(Symbol) || prepare.is_a?(String)
    if constant.respond_to?(prepare)
      constant.method(prepare.to_sym)
    end
  end
  type_for(
    type,
    ignore_nilable_wrapper: has_replaceable_default?(argument),
    prepare_method: prepare_method,
  )
end

def type_for_constant(constant)

: (Module constant) -> String
def type_for_constant(constant)
  if constant.instance_methods.include?(:prepare)
    prepare_method = constant.instance_method(:prepare)
    prepare_signature = Runtime::Reflection.signature_of(prepare_method)
    return prepare_signature.return_type&.to_s if valid_return_type?(prepare_signature&.return_type)
  end
  Runtime::Reflection.qualified_name_of(constant) || "T.untyped"
end

def valid_return_type?(return_type)

: (T::Types::Base? return_type) -> bool
def valid_return_type?(return_type)
  !!return_type && !(T::Private::Types::Void === return_type || T::Private::Types::NotTyped === return_type)
end