# frozen_string_literal: true

module ReleaseTools
  module Prometheus
    class Query
      include ::SemanticLogger::Loggable

      class PromQLError < StandardError
        attr_reader :status_code, :body

        def initialize(status_code, body)
          @status_code = status_code
          @body = body
        end

        def to_s
          "API failure. Status code: #{status_code}, body: #{body}"
        end
      end

      ENDPOINT = "#{ENV.fetch('MIMIR_URL', nil)}/api/v1/query".freeze
      GITLAB_GPRD_TENANT_ID = 'gitlab-gprd'
      GITLAB_GSTG_TENANT_ID = 'gitlab-gstg'
      GITLAB_OPS_TENANT_ID = 'gitlab-ops'

      # NOTE: Use Gitaly nodes since everything else is deployed via K8S
      # See https://gitlab.com/gitlab-com/gl-infra/delivery/-/issues/2169
      # rubocop:disable Layout/HashAlignment
      ROLE_VERSIONS = {
        'gprd'     => ['component="gitlab-rails", type="gitaly", env="gprd", stage="main"', GITLAB_GPRD_TENANT_ID],
        'gprd-cny' => ['component="gitlab-rails", type="gitaly", env="gprd", stage="cny"', GITLAB_GPRD_TENANT_ID],
        'gstg'     => ['component="gitlab-rails", type="gitaly", env="gstg", stage="main"', GITLAB_GSTG_TENANT_ID],
        'gstg-cny' => ['component="gitlab-rails", type="gitaly", env="gstg", stage="cny"', GITLAB_GSTG_TENANT_ID],
        'gstg-ref' => ['component="gitlab-rails", type="gitaly", env="gstg", stage="ref"', GITLAB_GSTG_TENANT_ID]
      }.freeze
      # rubocop:enable Layout/HashAlignment

      def run(query, tenant_id = GITLAB_GPRD_TENANT_ID)
        logger.trace('PromQL query', query: query)

        response = Retriable.retriable { get(query, tenant_id) }

        response.parse
      end

      def rails_version_by_role
        return {} unless ENV['MIMIR_URL']

        ROLE_VERSIONS.each_with_object({}) do |(role, params), memo|
          selector, tenant_id = params
          query = "topk(1, count(gitlab_version_info{#{selector}}) by (version))"
          begin
            response = run(query, tenant_id)

            # jq '.data .result | .[] | .metric'
            metric = response.dig('data', 'result', 0, 'metric')
            next memo[role] = '' if metric.nil?

            memo[role] = metric['version']
          rescue PromQLError
            next memo[role] = ''
          end
        end
      end

      def monthly_release_status(version)
        params = %{version="#{version}"}
        query = "last_over_time(delivery_release_monthly_status{#{params}}[30m])"

        metric_value_from_query(query)
      end

      def patch_release_status(versions)
        params = %{versions="#{versions}"}
        query = "last_over_time(delivery_release_patch_status{#{params}}[30m])"

        metric_value_from_query(query)
      end

      private

      def metric_value_from_query(query)
        return nil unless ENV['MIMIR_URL']

        # The delivery metrics services runs in ops, so tenant_id for release metrics needs to be 'gitlab-ops', not the default 'gitlab-gprd'
        response = run(query, GITLAB_OPS_TENANT_ID)

        # jq '.data .result | .[] | .value[1]'
        value = response.dig('data', 'result', 0, 'value', 1)

        logger.info(
          'Received metric value from query',
          query: query,
          value: value
        )

        value&.to_i
      end

      def get(query, tenant_id)
        response = client(tenant_id).get(ENDPOINT, params: { query: query })

        unless response.status.success?
          error = PromQLError.new(response.status.code, response.body.to_s)
          logger.warn('API Failure', status: error.status_code.to_s, error: error.body)

          raise error
        end

        response
      end

      def client(tenant_id)
        HTTP.headers(mimir_headers(tenant_id))
      end

      def mimir_headers(tenant_id)
        {}.tap do |headers|
          headers['X-Scope-OrgID'] = tenant_id
          headers['Authorization'] = "Basic #{Base64.strict_encode64("#{mimir_api_user}:#{mimir_api_password}")}"
        end
      end

      def mimir_api_user
        ENV.fetch('MIMIR_API_USER', nil)
      end

      def mimir_api_password
        ENV.fetch('MIMIR_API_PASSWORD', nil)
      end
    end
  end
end
