# frozen_string_literal: true

module RailsBestPractices
  module Reviews
    # Review config/routes.rb file to make sure not to use too deep nesting routes.
    #
    # See the best practice details here https://rails-bestpractices.com/posts/2010/07/22/needless-deep-nesting/
    #
    # Implementation:
    #
    # Review process:
    #   chech all method_add_block nodes in route file.
    #
    #   it is a recursively check in method_add_block node,
    #
    #   if it is a method_add_block node,
    #   increment @counter at the beginning of resources,
    #   decrement @counter at the end of resrouces,
    #   recursively check nodes in block body.
    #
    #   if the child node is a command_call or command node,
    #   and the message of the node is "resources" or "resource",
    #   and the @counter is greater than @nested_count defined,
    #   then it is a needless deep nesting.
    class NeedlessDeepNestingReview < Review
      interesting_nodes :method_add_block
      interesting_files ROUTE_FILES
      url 'https://rails-bestpractices.com/posts/2010/07/22/needless-deep-nesting/'

      def initialize(options = {})
        super(options)
        @counter = 0
        @nested_count = options['nested_count'] || 2
        @shallow_nodes = []
      end

      # check all method_add_block node.
      #
      # It is a recursively check, if it is a method_add_block node,
      # increment @counter at the beginning of resources,
      # decrement @counter at the end of method_add_block resources,
      # recursively check the block body.
      #
      # if the child node is a command_call or command node with message "resources" or "resource",
      # test if the @counter is greater than or equal to @nested_count,
      # if so, it is a needless deep nesting.
      add_callback :start_method_add_block do |node|
        @file = node.file
        recursively_check(node)
      end

      private

      # check nested route.
      #
      # if the receiver of the method_add_block is with message "resources" or "resource",
      # then increment the @counter, recursively check the block body, and decrement the @counter.
      #
      # if the node type is command_call or command,
      # and its message is resources or resource,
      # then check if @counter is greater than or equal to @nested_count,
      # if so, it is the needless deep nesting.
      def recursively_check(node)
        shallow = @shallow_nodes.include? node

        if %i[command_call command].include?(node[1].sexp_type) && %w[resources resource].include?(node[1].message.to_s)
          hash_node = node[1].arguments.grep_node(sexp_type: :bare_assoc_hash)
          shallow ||= (hash_node && hash_node.hash_value('shallow').to_s == 'true')
          @counter += 1
          node.block_node.statements.each do |stmt_node|
            @shallow_nodes << stmt_node if shallow
            recursively_check(stmt_node)
          end
          @counter -= 1
        elsif %i[command_call command].include?(node.sexp_type) && %w[resources resource].include?(node.message.to_s)
          add_error "needless deep nesting (nested_count > #{@nested_count})", @file, node.line_number if @counter >=
            @nested_count && !@shallow_nodes.include?(node)
        end
      end
    end
  end
end