# frozen_string_literal: true

module RuboCop
  module Cop
    module Gitlab
      # Detects the use of `.keys.first` or `.values.first` and suggests a
      # change to `.each_key.first` or `.each_value.first`. This reduces
      # memory usage and execution time.
      #
      # @example
      #
      #   # bad
      #
      #   hash.keys.first
      #   hash.values.first
      #
      #   # good
      #
      #   hash.each_key.first
      #   hash.each_value.first
      #
      class KeysFirstAndValuesFirst < RuboCop::Cop::Base
        extend RuboCop::Cop::AutoCorrector

        MSG = 'Prefer `.%{autocorrect_method}.first` over `.%{current_method}.first`. ' \
          'This reduces memory usage and execution time.'

        AUTOCORRECT_METHOD = {
          keys: 'each_key',
          values: 'each_value'
        }.freeze

        # @!method keys_or_values_first(node)
        def_node_matcher :keys_or_values_first, <<~PATTERN
          (send (send ({send const hash lvar} ...) ${:keys :values}) :first)
        PATTERN

        def on_send(node)
          current_method = keys_or_values_first(node)
          return unless current_method

          autocorrect_method = AUTOCORRECT_METHOD.fetch(current_method)
          msg = format(MSG, autocorrect_method: autocorrect_method, current_method: current_method)

          add_offense(node.loc.selector, message: msg) do |corrector|
            replacement = "#{autocorrect_expression(node)}.#{autocorrect_method}.first"
            corrector.replace(node, replacement)
          end
        end

        private

        def autocorrect_expression(node)
          node.receiver.receiver.source
        end
      end
    end
  end
end
