# frozen_string_literal: true

module Gitlab
  module Dangerfiles
    class Teammate
      attr_reader :options, :username, :name, :role, :projects, :available, :hungry, :reduced_capacity, :tz_offset_hours

      # The options data are produced by https://gitlab.com/gitlab-org/gitlab-roulette/-/blob/master/lib/team_member.rb
      def initialize(options = {})
        @options = options
        @username = options["username"]
        @name = options["name"]
        @markdown_name = options["markdown_name"]
        @role = options["role"]
        @projects = process_projects(options["projects"])
        @available = options["available"]
        @hungry = options["hungry"]
        @reduced_capacity = options["reduced_capacity"]
        @tz_offset_hours = options["tz_offset_hours"]
      end

      def to_h
        options
      end

      def ==(other)
        return false unless other.respond_to?(:username)

        other.username == username
      end

      def in_project?(name)
        projects&.has_key?(name)
      end

      def any_capability?(project, category)
        capabilities(project).any? { |capability| capability.end_with?(category.to_s) }
      end

      def reviewer?(project, category, labels)
        has_capability?(project, category, :reviewer, labels)
      end

      def traintainer?(project, category, labels)
        has_capability?(project, category, :trainee_maintainer, labels)
      end

      def maintainer?(project, category, labels)
        has_capability?(project, category, :maintainer, labels)
      end

      def integrations_be?(project, category, labels)
        return false unless category == :integrations_be

        has_capability?(project, category, :reviewer, labels)
      end

      def integrations_fe?(project, category, labels)
        return false unless category == :integrations_fe

        has_capability?(project, category, :reviewer, labels)
      end

      def markdown_name(author: nil)
        "#{@markdown_name} (#{utc_offset_text(author)})"
      end

      def local_hour
        (Time.now.utc + tz_offset_hours * 3600).hour
      end

      protected

      def floored_offset_hours
        floored_offset = tz_offset_hours.floor(0)

        floored_offset == tz_offset_hours ? floored_offset : tz_offset_hours
      end

      private

      def process_projects(projects)
        return nil unless projects

        projects.each_with_object({}) do |(project, capabilities), all|
          all[project.downcase] = Array(capabilities).map(&:downcase)
        end
      end

      def utc_offset_text(author = nil)
        offset_text = if floored_offset_hours >= 0
            "UTC+#{floored_offset_hours}"
          else
            "UTC#{floored_offset_hours}"
          end

        return offset_text unless author

        "#{offset_text}, #{offset_diff_compared_to_author(author)}"
      end

      def offset_diff_compared_to_author(author)
        diff = floored_offset_hours - author.floored_offset_hours
        return "same timezone as `@#{author.username}`" if diff == 0

        ahead_or_behind = diff < 0 ? "behind" : "ahead of"
        pluralized_hours = pluralize(diff.abs, "hour", "hours")

        "#{pluralized_hours} #{ahead_or_behind} `@#{author.username}`"
      end

      def has_capability?(project, category, kind, labels)
        case category
        when :test
          area = role[/Software Engineer in Test(?:.*?, (\w+))/, 1]

          area && labels.any?("devops::#{area.downcase}") if kind == :reviewer
        when :tooling, :engineering_productivity # Deprecated as of 2.3.0 in favor of tooling
          return true if capabilities(project).include?("#{kind} #{category}")
          return false if kind == :maintainer

          capabilities(project).include?("#{kind} backend") # fallback to backend reviewer
        when :integrations_be
          kind == :reviewer &&
            role.match?(/Backend Engineer.+Ecosystem:Integrations/)
        when :integrations_fe
          kind == :reviewer &&
            role.match?(/Frontend Engineer.+Ecosystem:Integrations/)
        when nil
          capabilities(project).include?("#{kind}")
        else
          capabilities(project).include?("#{kind} #{category}")
        end || has_backup_capability?(category, kind, labels)
      end

      def has_backup_capability?(category, kind, labels)
        case category
        when :ux
          capacity = "#{kind} #{category}"

          projects.each_value.find do |capabilities|
            capabilities.include?(capacity)
          end
        end
      end

      def capabilities(project)
        projects.fetch(project, [])
      end

      def pluralize(count, singular, plural)
        word = count == 1 || count.to_s =~ /^1(\.0+)?$/ ? singular : plural

        "#{count || 0} #{word}"
      end
    end
  end
end