require "gitlab/dangerfiles/version"

module Gitlab
  module Dangerfiles
    RULES_DIR = File.expand_path("../danger/rules", __dir__)
    EXISTING_RULES = Dir.glob(File.join(RULES_DIR, "*")).each_with_object([]) do |path, memo|
      if File.directory?(path)
        memo << File.basename(path)
      end
    end
    LOCAL_RULES = %w[
      changes_size
      commit_messages
    ].freeze
    CI_ONLY_RULES = %w[
      simple_roulette
    ].freeze

    # Utility method to construct a [Gitlab::Dangerfiles::Engine] instance,
    # which is yielded to the given block.
    #
    # @param dangerfile [Danger::Dangerfile] A +Danger::Dangerfile+ object.
    # @param project_name An option string to set the project name. Defaults to +ENV['CI_PROJECT_NAME']+.
    #
    # @return [Gitlab::Dangerfiles::Engine]
    def self.for_project(dangerfile, project_name = nil)
      Engine.new(dangerfile).tap do |engine|
        engine.config.project_name = project_name if project_name

        yield engine
      end
    end

    # This class provides utility methods to import plugins and dangerfiles easily.
    class Engine
      # @param dangerfile [Danger::Dangerfile] A +Danger::Dangerfile+ object.
      #
      # @example
      #   # In your main Dangerfile:
      #   dangerfiles = Gitlab::Dangerfiles::Engine.new(self)
      #
      # @return [Gitlab::Dangerfiles::Engine]
      def initialize(dangerfile)
        @dangerfile = dangerfile

        # Import internal plugins eagerly, since other functionality in this class may depend on them.
        danger_plugin.import_plugin(File.expand_path("../danger/plugins/internal/*.rb", __dir__))
      end

      # Import all available plugins.
      #
      # @example
      #   # In your main Dangerfile:
      #   Gitlab::Dangerfiles.for_project(self) do |dangerfiles|
      #     dangerfiles.import_plugins
      #   end
      def import_plugins
        danger_plugin.import_plugin(File.expand_path("../danger/plugins/*.rb", __dir__))
      end

      # Import available Dangerfiles.
      #
      # @deprecated
      # @param rules [Symbol, Array<String>] Can be either +:all+ (default) to import all rules,
      #   or an array of rules.
      #   Available rules are: +changes_size+.
      #
      # @param only [Symbol, Array<String>] An array of rules to import (defaults to all rules).
      #   Available rules are: +changes_size+.
      #
      # @param except [Symbol, Array<String>] An array of rules to not import (defaults to []).
      #   Available rules are: +changes_size+.
      #
      # @example
      #   # In your main Dangerfile:
      #   Gitlab::Dangerfiles.for_project(self) do |dangerfiles|
      #     # Import all rules
      #     dangerfiles.import_dangerfiles
      #     # Or import only a subset of rules
      #     dangerfiles.import_dangerfiles(only: %w[changes_size])
      #     # Or import all rules except a subset of rules
      #     dangerfiles.import_dangerfiles(except: %w[commit_messages])
      #     # Or import only a subset of rules, except a subset of rules
      #     dangerfiles.import_dangerfiles(only: %w[changes_size], except: %w[commit_messages])
      #   end
      def import_dangerfiles(rules: nil, only: nil, except: [])
        puts "The `:rules` parameter is deprecated in favor of `:only`." unless rules.nil?

        only ||= EXISTING_RULES if rules == :all
        only ||= rules || EXISTING_RULES

        filtered_rules(only, except).each do |rule|
          danger_plugin.import_dangerfile(path: File.join(RULES_DIR, rule))
        end
      end

      # Proxy method to +helper_plugin.config+.
      def config
        helper_plugin.config
      end

      # Imports all default plugins and rules.
      #
      # @example
      #   # In your main Dangerfile:
      #   Gitlab::Dangerfiles.for_project(self) do |dangerfiles|
      #     dangerfiles.import_defaults
      #   end
      def import_defaults
        import_plugins
        import_dangerfiles
      end

      private

      attr_reader :dangerfile

      def allowed_rules
        return LOCAL_RULES unless helper_plugin.respond_to?(:ci?)

        helper_plugin.ci? ? LOCAL_RULES | CI_ONLY_RULES : LOCAL_RULES
      end

      def filtered_rules(only, except)
        (Array(only).map(&:to_s) & EXISTING_RULES & allowed_rules) - except
      end

      def danger_plugin
        @danger_plugin ||= dangerfile.plugins[Danger::DangerfileDangerPlugin]
      end

      def helper_plugin
        @helper_plugin ||= dangerfile.plugins[Danger::Helper]
      end
    end
  end
end