# frozen_string_literal: true

module ReleaseTools
  module Promotion
    module Checks
      # GitlabDeploymentHealth checks our production metrics to allow a deployment
      class GitlabDeploymentHealth
        include ::ReleaseTools::Promotion::Check
        include ::SemanticLogger::Loggable

        HEADING = ':crystal_ball: GitLab Deployment Health Status'

        def name
          'services health'
        end

        def fine?
          !ok_count.zero? && ok_count == services.size
        end

        def to_issue_body
          text = StringIO.new

          text.puts("#{HEADING} - [overview](#{current_grafana_url})\n\n")

          if fine?
            text.puts("* #{ok_icon} All services healthy")
          else
            services_status_markup.each do |status|
              text.puts("* #{status}")
            end
          end

          text.string
        end

        def to_slack_blocks
          [
            {
              type: 'section',
              text: mrkdwn("#{HEADING} - #{Slack::Utilities.api_link('overview', current_grafana_url)}")
            },
            {
              type: 'context',
              elements: services_status_markup.map { |status| mrkdwn(status) }
            }
          ]
        end

        ErrorService = Struct.new(:exception) do
          def fine?
            false
          end

          def name
            "Cannot detect gitlab deployment health, #{exception.message}"
          end
        end

        def current_grafana_url
          Mimir::Utils.explore_pane_url(
            query: 'gitlab_deployment_health:service{env="gprd"}',
            from: 'now-6h',
            to: 'now'
          )
        end

        def services
          @services ||=
            begin
              ::ReleaseTools::Prometheus::ServiceHealth.new.services
            rescue StandardError => ex
              logger.error('Cannot detect gitlab deployment health', error: ex)

              ::ReleaseTools::ErrorTracking.capture_exception(ex)

              [ErrorService.new(ex)]
            end
        end

        def ok_count
          services.count(&:fine?)
        end

        # services_status_markup produces a markup compatible with both
        # GitLab markdown and Slack mrkdwn syntax.
        #
        # @return [Array<String>] one markup line for each service
        def services_status_markup
          return ["#{failure_icon} no data"] if services.empty?

          services.map do |service|
            "#{icon(service)} #{service.name}"
          end
        end
      end
    end
  end
end
