# frozen_string_literal: true

module RuboCop
  module Cop
    module Style
      # This cop checks for places where `case-when` represents a simple 1:1
      # mapping and can be replaced with a hash lookup.
      #
      # @example MinBranchesCount: 3 (default)
      #   # bad
      #   case country
      #   when 'europe'
      #     'http://eu.example.com'
      #   when 'america'
      #     'http://us.example.com'
      #   when 'australia'
      #     'http://au.example.com'
      #   end
      #
      #   # good
      #   SITES = {
      #     'europe'    => 'http://eu.example.com',
      #     'america'   => 'http://us.example.com',
      #     'australia' => 'http://au.example.com'
      #   }
      #   SITES[country]
      #
      # @example MinBranchesCount: 4
      #   # good
      #   case country
      #   when 'europe'
      #     'http://eu.example.com'
      #   when 'america'
      #     'http://us.example.com'
      #   when 'australia'
      #     'http://au.example.com'
      #   end
      #
      class HashLikeCase < Base
        MSG = 'Consider replacing `case-when` with a hash lookup.'

        def_node_matcher :hash_like_case?, <<~PATTERN
          (case
            _
            (when
              ${str_type? sym_type?}
              $[!nil? recursive_basic_literal?])+ nil?)
        PATTERN

        def on_case(node)
          return if node.when_branches.size < min_branches_count

          hash_like_case?(node) do |condition_nodes, body_nodes|
            if nodes_of_same_type?(condition_nodes) &&
               nodes_of_same_type?(body_nodes)
              add_offense(node)
            end
          end
        end

        private

        def nodes_of_same_type?(nodes)
          nodes.all? { |node| node.type == nodes.first.type }
        end

        def min_branches_count
          length = cop_config['MinBranchesCount'] || 3
          return length if length.is_a?(Integer) && length.positive?

          raise 'MinBranchesCount needs to be a positive integer!'
        end
      end
    end
  end
end
