# frozen_string_literal: true

# This file contains patches to RuboCop to support
# edge features and fix some bugs with 2.7+ syntax

require "parser/ruby-next/version"
require "ruby-next/config"
require "ruby-next/language/parser"

module RuboCop
  # Transform Ruby Next parser version to a float, e.g.: "2.8.0.1" => 2.801
  RUBY_NEXT_VERSION = Parser::NEXT_VERSION.match(/(^\d+)\.(.+)$/)[1..-1].map { |part| part.delete(".") }.join(".").to_f

  class TargetRuby
    class RuboCopNextConfig < RuboCopConfig
      private

      def find_version
        version = @config.for_all_cops["TargetRubyVersion"]
        return unless version == "next"

        RUBY_NEXT_VERSION
      end
    end

    new_rubies = KNOWN_RUBIES + [RUBY_NEXT_VERSION]
    remove_const :KNOWN_RUBIES
    const_set :KNOWN_RUBIES, new_rubies

    new_sources = [RuboCopNextConfig] + SOURCES
    remove_const :SOURCES
    const_set :SOURCES, new_sources
  end
end

module RuboCop
  class ProcessedSource
    module ParserClassExt
      def parser_class(version, *)
        return super unless version == RUBY_NEXT_VERSION

        require "parser/rubynext"
        Parser::RubyNext
      end
    end

    prepend ParserClassExt
  end
end

# Let's make this file Ruby 2.2 compatible to avoid transpiling
# rubocop:disable Layout/HeredocIndentation
module RuboCop
  module AST
    module Traversal
      # Fixed in https://github.com/rubocop-hq/rubocop/pull/7786
      %i[case_match in_pattern find_pattern match_pattern match_pattern_p].each do |type|
        next if method_defined?(:"on_#{type}")
        module_eval(<<-RUBY, __FILE__, __LINE__ + 1)
def on_#{type}(node)
node.children.each { |child| send(:"on_\#{child.type}", child) if child }
nil
end
        RUBY
      end
    end

    unless Builder.method_defined?(:match_pattern_p)
      Builder.include RubyNext::Language::BuilderExt
    end
  end
end
# rubocop:enable Layout/HeredocIndentation

module RuboCop
  module Cop
    # Commissioner class is responsible for processing the AST and delegating
    # work to the specified cops.
    class Commissioner
      def on_meth_ref(node)
        trigger_responding_cops(:on_meth_ref, node)
      end

      unless method_defined?(:on_numblock)
        def on_numblock(node)
          children = node.children
          child = children[0]
          send(:"on_#{child.type}", child)
          # children[1] is the number of parameters
          return unless (child = children[2])

          send(:"on_#{child.type}", child)
        end
      end

      unless method_defined?(:on_def_e)
        def on_def_e(node)
          _name, _args_node, body_node = *node
          send(:"on_#{body_node.type}", body_node)
        end

        def on_defs_e(node)
          _definee_node, _name, _args_node, body_node = *node
          send(:"on_#{body_node.type}", body_node)
        end
      end
    end

    Commissioner.prepend(Module.new do
      # Ignore anonymous blocks
      def on_block_pass(node)
        return if node.children == [nil]

        super
      end

      def on_blockarg(node)
        return if node.children == [nil]

        super
      end
    end)

    module Layout
      require "rubocop/cop/layout/assignment_indentation"

      POTENTIAL_RIGHT_TYPES = %i[ivasgn lvasgn cvasgn gvasgn casgn masgn].freeze

      AssignmentIndentation.prepend(Module.new do
        def check_assignment(node, *)
          return if rightward?(node)
          super
        end

        private

        def rightward?(node)
          return unless POTENTIAL_RIGHT_TYPES.include?(node.type)

          return unless node.loc.operator

          assignee_loc =
            if node.type == :masgn
              node.children[0].loc.expression
            else
              node.loc.name
            end

          return false unless assignee_loc

          assignee_loc.begin_pos > node.loc.operator.end_pos
        end
      end)

      require "rubocop/cop/layout/empty_line_between_defs"
      EmptyLineBetweenDefs.prepend(Module.new do
        def def_end(node)
          return super unless node.loc.end.nil?

          node.loc.expression.line
        end
      end)

      require "rubocop/cop/layout/space_after_colon"
      SpaceAfterColon.prepend(Module.new do
        def on_pair(node)
          return if node.children[0].loc.last_column == node.children[1].loc.last_column

          super(node)
        end
      end)
    end

    module Style
      require "rubocop/cop/style/single_line_methods"
      SingleLineMethods.prepend(Module.new do
        def on_def(node)
          return if node.loc.end.nil?
          super
        end

        def on_defs(node)
          return if node.loc.end.nil?
          super
        end
      end)

      require "rubocop/cop/style/def_with_parentheses"
      DefWithParentheses.prepend(Module.new do
        def on_def(node)
          return if node.loc.end.nil?
          super
        end

        def on_defs(node)
          return if node.loc.end.nil?
          super
        end
      end)

      require "rubocop/cop/style/trailing_method_end_statement"
      TrailingMethodEndStatement.prepend(Module.new do
        def on_def(node)
          return if node.loc.end.nil?
          super
        end
      end)
    end
  end
end