# frozen_string_literal: true

module ReleaseTools
  module Metrics
    class ReleaseMetricUpdater
      include ::SemanticLogger::Loggable

      class InvalidReleaseStatusError < StandardError
        def initialize(status_code)
          super("Invalid release status: #{status_code}")
        end
      end

      MONTHLY_STATUSES = %i[open announced tagged_rc].freeze
      PATCH_STATUSES = %i[open warning closed].freeze

      def initialize
        @prometheus = ReleaseTools::Prometheus::Query.new
      end

      def execute
        return unless Feature.enabled?(:release_status_metric_update)

        update_monthly_release_status
        update_patch_release_status
      end

      private

      def releases_client
        @releases_client ||= ReleaseTools::GitlabReleasesGemClient
      end

      # releases_client.current version gets the current deployed version, sourced from versions.gitlab.com
      # releases.client.active_version gets the next version to be deployed, sourced from releases.yml (https://gitlab.com/gitlab-com/www-gitlab-com/-/blob/master/data/releases.yml)
      # This would return true only on the release day, after the new version has been created on versions.gitlab.com
      def release_day?
        releases_client.current_version == releases_client.active_version
      end

      # Monthly active version
      def monthly_active_version
        version = if release_day?
                    # On the release day, after the release has been published,
                    # we need to get the version for tomorrow
                    # This will fail before
                    releases_client.version_for_date(Date.tomorrow)
                  else
                    releases_client.active_version
                  end

        ReleaseTools::Version.new(version).to_minor
      end

      def next_patch_release_date
        @next_patch_release_date ||= releases_client.next_patch_release_date
      end

      def patch_next_versions
        # Finds the current minor version when the next patch release is scheduled
        # Then finds the previous minor versions for backports

        minor_version_for_patch_release = releases_client.current_minor_for_date(next_patch_release_date)

        releases_client
          .previous_minors(minor_version_for_patch_release)
          .join(" ")
      end

      def monthly_release_status(version)
        value = @prometheus.monthly_release_status(version)
        validate_release_status(value, MONTHLY_STATUSES)
        value
      end

      def patch_release_status(versions)
        value = @prometheus.patch_release_status(versions)
        validate_release_status(value, PATCH_STATUSES)
        value
      end

      def validate_release_status(value, statuses)
        raise InvalidReleaseStatusError, value unless value.is_a?(Integer) && value.positive? && value <= statuses.size
      end

      def update_monthly_release_status
        monthly_status = monthly_release_status(monthly_active_version)

        # Status on the MonthlyReleaseStatus metric
        # 1 = open, 2 = announced, 3 = tagged_rc
        # so we need to subtract 1 to match it to MONTHLY_STATUSES
        status = MONTHLY_STATUSES[monthly_status - 1]
        ReleaseTools::Metrics::MonthlyReleaseStatus
          .new(status: status)
          .execute
      end

      def update_patch_release_status
        patch_status = patch_release_status(patch_next_versions)

        # Status on the PatchReleaseStatus metric
        # 1 = open, 2 = warning, 3 = closed
        # so we need to subtract 1 to match it to PATCH_STATUSES
        status = PATCH_STATUSES[patch_status - 1]
        ReleaseTools::Metrics::PatchReleaseStatus
          .new(status: status)
          .execute
      end
    end
  end
end
