# frozen_string_literal: true

module Gitlab
  module Dangerfiles
    CategoryStruct = Struct.new(:name, :project, :kind, :labels, keyword_init: true)

    class Category < CategoryStruct
      def self.for(name, **arguments)
        (name_to_class[name] || self).new(name: name, **arguments)
      end

      def self.name_to_class
        @name_to_class ||= {
          none: None,
          test: Test,
          tooling: Tooling,
          import_integrate_be: ImportIntegrateBE,
          import_integrate_fe: ImportIntegrateFE,
          ux: UX,
        }.freeze
      end
      private_class_method :name_to_class

      def has_capability?(teammate)
        teammate.capabilities(project).include?(capability)
      end

      private

      def capability
        @capability ||= "#{kind} #{name}"
      end

      class None < Category
        def capability
          @capability ||= kind.to_s
        end
      end

      class Test < Category
        def has_capability?(teammate)
          return false if kind != :reviewer

          area = teammate.role[/Software Engineer in Test(?:.*?, (\w+))/, 1]

          !!area && labels.any?("devops::#{area.downcase}")
        end
      end

      class Tooling < Category
        def has_capability?(teammate)
          if super
            true
          elsif %i[trainee_maintainer maintainer].include?(kind)
            false
          else # fallback to backend reviewer
            teammate.capabilities(project).include?("#{kind} backend")
          end
        end
      end

      class ImportIntegrateBE < Category
        def has_capability?(teammate)
          kind == :reviewer &&
            teammate.role.match?(/Backend Engineer.+Manage:Import and Integrate/)
        end
      end

      class ImportIntegrateFE < Category
        def has_capability?(teammate)
          kind == :reviewer &&
            teammate.role.match?(/Frontend Engineer.+Manage:Import and Integrate/)
        end
      end

      class UX < Category
        def has_capability?(teammate)
          super &&

            if labels.any?("Community contribution")
              # We want the designer for the team to review the wider community
              # contribution because they're more familiar with that area.
              the_designer_for_the_team?(teammate)
            else
              # We don't want the designer for the team to review merge
              # requests for the same team which is designed by themselves.
              # So they can only review if they're not the designer for the team.
              !the_designer_for_the_team?(teammate)
            end
        end

        private

        def the_designer_for_the_team?(teammate)
          # Pick corresponding group for community contribution
          # Specialty can be:
          # Source Code
          # [Growth: Activation, Growth: Expansion]
          # Runner
          group_labels = Array(teammate.specialty).map do |field|
            group = field.strip.sub(/^.+: ?/, "").downcase

            "group::#{group}"
          end

          (group_labels & labels).any?
        end
      end
    end
  end
end