# frozen_string_literal: true

module RuboCop
  module Cop
    module Layout
      # This cop enforces empty line after multiline condition.
      #
      # @example
      #   # bad
      #   if multiline &&
      #     condition
      #     do_something
      #   end
      #
      #   # good
      #   if multiline &&
      #     condition
      #
      #     do_something
      #   end
      #
      #   # bad
      #   case x
      #   when foo,
      #     bar
      #     do_something
      #   end
      #
      #   # good
      #   case x
      #   when foo,
      #     bar
      #
      #     do_something
      #   end
      #
      #   # bad
      #   begin
      #     do_something
      #   rescue FooError,
      #     BarError
      #     handle_error
      #   end
      #
      #   # good
      #   begin
      #     do_something
      #   rescue FooError,
      #     BarError
      #
      #     handle_error
      #   end
      #
      class EmptyLineAfterMultilineCondition < Base
        include RangeHelp
        extend AutoCorrector

        MSG = 'Use empty line after multiline condition.'

        def on_if(node)
          return if node.ternary?

          if node.modifier_form?
            check_condition(node.condition) if node.right_sibling
          else
            check_condition(node.condition)
          end
        end

        def on_while(node)
          check_condition(node.condition)
        end
        alias on_until on_while

        def on_while_post(node)
          return unless node.right_sibling

          check_condition(node.condition)
        end
        alias on_until_post on_while_post

        def on_case(node)
          node.each_when do |when_node|
            last_condition = when_node.conditions.last

            next if !multiline_when_condition?(when_node) ||
                    next_line_empty?(last_condition.last_line)

            add_offense(when_node, &autocorrect(last_condition))
          end
        end

        def on_rescue(node)
          node.resbody_branches.each do |resbody|
            rescued_exceptions = resbody.exceptions
            next if !multiline_rescue_exceptions?(rescued_exceptions) ||
                    next_line_empty?(rescued_exceptions.last.last_line)

            add_offense(resbody, &autocorrect(rescued_exceptions.last))
          end
        end

        private

        def check_condition(condition)
          return unless condition.multiline?
          return if next_line_empty?(condition.last_line)

          add_offense(condition, &autocorrect(condition))
        end

        def next_line_empty?(line)
          processed_source[line].blank?
        end

        def multiline_when_condition?(when_node)
          when_node.conditions.first.first_line != when_node.conditions.last.last_line
        end

        def multiline_rescue_exceptions?(exception_nodes)
          return false if exception_nodes.size <= 1

          first, *_rest, last = *exception_nodes
          first.first_line != last.last_line
        end

        def autocorrect(node)
          lambda do |corrector|
            range = range_by_whole_lines(node.source_range)
            corrector.insert_after(range, "\n")
          end
        end
      end
    end
  end
end
