# frozen_string_literal: true

module RailsBestPractices
  module Reviews
    # Review a controller file to make sure that complex model creation should not exist in
    # controller, should be replaced with factory method.
    #
    # See the best practice details here https://rails-bestpractices.com/posts/2010/07/21/replace-complex-creation-with-factory-method/
    #
    # Implementation:
    #
    # Review process:
    #   check all method defines in the controller files,
    #   if there are multiple attribute assignments apply to one receiver,
    #   and the receiver is a variable,
    #   and after them there is a call node with message "save" or "save!",
    #   then these attribute assignments are complex creation, should be replaced with factory method.
    class ReplaceComplexCreationWithFactoryMethodReview < Review
      interesting_nodes :def
      interesting_files CONTROLLER_FILES
      url 'https://rails-bestpractices.com/posts/2010/07/21/replace-complex-creation-with-factory-method/'

      def initialize(options = {})
        super(options)
        @assigns_count = options['attribute_assignment_count'] || 2
      end

      # check method define node to see if there are multiple assignments, more than
      # @assigns_count, on one variable before save.
      #
      # it wll check every attrasgn nodes in method define node,
      # if there are multiple assign nodes who have the same receiver,
      # and the receiver is a variable,
      # and after them, there is a call node with message "save" or "save!",
      # then these attribute assignments are complex creation, should be replaced with factory method.
      add_callback :start_def do |node|
        node.recursive_children do |child_node|
          case child_node.sexp_type
          when :assign
            if child_node.receiver[2].to_s == '.'
              remember_variable_use_count(child_node)
            end
          when :call
            check_variable_save(child_node)
          end
        end
        reset_variable_use_count
      end

      private

      # check the call node to see if it is with message "save" or "save!",
      # and the count attribute assignment on the receiver of the call node is greater than @assign_count defined,
      # then it is a complex creation, should be replaced with factory method.
      def check_variable_save(node)
        if ['save', 'save!'].include? node.message.to_s
          variable = node.receiver.to_s
          if variable_use_count[variable].to_i > @assigns_count
            hint = "#{variable} attribute_assignment_count > #{@assigns_count}"
            add_error "replace complex creation with factory method (#{hint})"
          end
        end
      end
    end
  end
end
