# frozen_string_literal: true
require "graphql/relay/mutation/instrumentation"
require "graphql/relay/mutation/resolve"
require "graphql/relay/mutation/result"

module GraphQL
  module Relay
    # @api deprecated
    class Mutation
      include GraphQL::Define::InstanceDefinable
      accepts_definitions(
        :name, :description, :resolve,
        :return_type,
        :return_interfaces,
        input_field: GraphQL::Define::AssignArgument,
        return_field: GraphQL::Define::AssignObjectField,
        function: GraphQL::Define::AssignMutationFunction,
      )
      attr_accessor :name, :description, :fields, :arguments
      attr_writer :return_type, :return_interfaces

      ensure_defined(
        :input_fields, :return_fields, :name, :description,
        :fields, :arguments, :return_type,
        :return_interfaces, :resolve=,
        :field, :result_class, :input_type
      )
      # For backwards compat, but do we need this separate API?
      alias :return_fields :fields
      alias :input_fields :arguments

      def initialize
        @fields = {}
        @arguments = {}
        @has_generated_return_type = false
      end

      def has_generated_return_type?
        # Trigger the generation of the return type, if it is dynamically generated:
        return_type
        @has_generated_return_type
      end

      def resolve=(new_resolve_proc)
        @resolve_proc = new_resolve_proc
      end

      def field
        @field ||= begin
          relay_mutation = self
          field_resolve_proc = @resolve_proc
          GraphQL::Field.define do
            type(relay_mutation.return_type)
            description(relay_mutation.description)
            argument :input, !relay_mutation.input_type
            resolve(field_resolve_proc)
            mutation(relay_mutation)
          end
        end
      end

      def return_interfaces
        @return_interfaces ||= []
      end

      def return_type
        @return_type ||= begin
          @has_generated_return_type = true
          relay_mutation = self
          GraphQL::ObjectType.define do
            name("#{relay_mutation.name}Payload")
            description("Autogenerated return type of #{relay_mutation.name}")
            field :clientMutationId, types.String, "A unique identifier for the client performing the mutation.", property: :client_mutation_id
            interfaces relay_mutation.return_interfaces
            relay_mutation.return_fields.each do |name, field_obj|
              field name, field: field_obj
            end
            mutation(relay_mutation)
          end
        end
      end

      def input_type
        @input_type ||= begin
          relay_mutation = self
          input_object_type = GraphQL::InputObjectType.define do
            name("#{relay_mutation.name}Input")
            description("Autogenerated input type of #{relay_mutation.name}")
            input_field :clientMutationId, types.String, "A unique identifier for the client performing the mutation."
            mutation(relay_mutation)
          end
          input_fields.each do |name, arg|
            input_object_type.arguments[name] = arg
          end

          input_object_type
        end
      end

      def result_class
        @result_class ||= Result.define_subclass(self)
      end
    end
  end
end
