# frozen_string_literal: true

module RailsBestPractices
  module Reviews
    # Review a controller to make sure to use scope access instead of manually checking current_user and redirect.
    #
    # See the best practice details here https://rails-bestpractices.com/posts/2010/07/20/use-scope-access/
    #
    # Implementation:
    #
    # Review process:
    #   check all if nodes to see
    #
    #   if they are compared with current_user or current_user.id,
    #   and there is redirect_to method call in if block body,
    #   then it should be replaced by using scope access.
    class UseScopeAccessReview < Review
      interesting_nodes :if, :unless, :elsif, :ifop, :if_mod, :unless_mod
      interesting_files CONTROLLER_FILES
      url 'https://rails-bestpractices.com/posts/2010/07/20/use-scope-access/'

      # check if node.
      #
      # if it is a method call compared with current_user or current_user.id,
      # and there is a redirect_to method call in the block body,
      # then it should be replaced by using scope access.
      add_callback :start_if, :start_unless, :start_elsif, :start_ifop, :start_if_mod, :start_unless_mod do |node|
        add_error 'use scope access' if current_user_redirect?(node)
      end

      private

      # check a if node to see
      #
      # if the conditional statement is compared with current_user or current_user.id,
      # and there is a redirect_to method call in the block body,
      # then it should be replaced by using scope access.
      def current_user_redirect?(node)
        all_conditions =
          if node.conditional_statement == node.conditional_statement.all_conditions
            [node.conditional_statement]
          else
            node.conditional_statement.all_conditions
          end
        results =
          all_conditions.map do |condition_node|
            ['==', '!='].include?(condition_node.message.to_s) &&
              (current_user?(condition_node.argument) || current_user?(condition_node.receiver))
          end
        results.any? { |result| result == true } && node.body.grep_node(message: 'redirect_to')
      end

      # check a call node to see if it uses current_user, or current_user.id.
      def current_user?(node)
        node.to_s == 'current_user' || (node.receiver.to_s == 'current_user' && node.message.to_s == 'id')
      end
    end
  end
end
