# frozen_string_literal: true

module RuboCop
  module Cop
    module Discourse
      module Plugins
        # Plugin controllers must call `requires_plugin` to prevent routes from
        # being accessible when the plugin is disabled.
        #
        # @example
        #   # bad
        #   class MyController
        #     def my_action
        #     end
        #   end
        #
        #   # good
        #   class MyController
        #     requires_plugin PLUGIN_NAME
        #     def my_action
        #     end
        #   end
        #
        class CallRequiresPlugin < Base
          MSG =
            "Use `requires_plugin` in controllers to prevent routes from being accessible when plugin is disabled."

          def_node_matcher :requires_plugin_present?, <<~MATCHER
            (class _ _
              {
                (begin <(send nil? :requires_plugin _) ...>)
                <(send nil? :requires_plugin _) ...>
              }
            )
          MATCHER

          def on_class(node)
            return if requires_plugin_present?(node)
            return if requires_plugin_present_in_parent_classes(node)
            add_offense(node, message: MSG)
          end

          private

          def requires_plugin_present_in_parent_classes(node)
            return unless processed_source.path
            controller_path = base_controller_path(node.parent_class&.const_name.to_s)
            return unless controller_path
            Commissioner
              .new([self.class.new(config, @options)])
              .investigate(parse(controller_path.read, controller_path.to_s))
              .offenses
              .empty?
          end

          def base_controller_path(base_class)
            return if base_class.blank?
            base_path = "#{base_class.underscore}.rb"
            path = Pathname.new("#{processed_source.path}/../").cleanpath
            until path.root?
              controller_path = path.join(base_path)
              return controller_path if controller_path.exist?
              path = path.join("..").cleanpath
            end
          end
        end
      end
    end
  end
end
