# frozen_string_literal: true

require_relative 'base_matcher'
require_relative './have_a_field_matchers/of_type'

module RSpec
  module GraphqlMatchers
    class AcceptArgument < BaseMatcher
      def initialize(expected_arg_name)
        @expectations = []

        if expected_arg_name.is_a?(Hash)
          (expected_arg_name, expected_type) = expected_arg_name.to_a.first
          of_type(expected_type)

          warn 'DEPRECATION WARNING: using accept_arguments with a hash will be '\
               'deprecated on the next major release. Use the format ' \
               "`accept_argument(:argument_name).of_type('ExpectedType!') instead.`"
        end

        @expected_arg_name = expected_arg_name.to_s
        @expected_camel_arg_name = GraphQL::Schema::Member::BuildType.camelize(
          @expected_arg_name
        )
      end

      def matches?(graph_object)
        @graph_object = graph_object

        @actual_argument = field_arguments[@expected_arg_name]
        @actual_argument ||= field_arguments[@expected_camel_arg_name]
        return false if @actual_argument.nil?

        @results = @expectations.reject do |matcher|
          matcher.matches?(@actual_argument)
        end

        @results.empty?
      end

      def of_type(expected_field_type)
        @expectations << HaveAFieldMatchers::OfType.new(expected_field_type)
        self
      end

      def failure_message
        base_msg = "expected #{member_name(@graph_object)} " \
          "to accept argument `#{@expected_arg_name}`" \

        return "#{base_msg} #{failure_messages.join(', ')}" if @actual_argument

        "#{base_msg} but no argument was found with that name"
      end

      def description
        ["accept argument `#{@expected_arg_name}`"].concat(descriptions).join(', ')
      end

      private

      def descriptions
        results.map(&:description)
      end

      def failure_messages
        results.map(&:failure_message)
      end

      def field_arguments
        if @graph_object.respond_to?(:arguments)
          @graph_object.public_send(:arguments)
        else
          raise "Invalid object #{@graph_object} provided to accept_argument " \
            'matcher. It does not seem to be a valid GraphQL object type.'
        end
      end

      def results
        @results ||= []
      end
    end
  end
end
