# frozen_string_literal: true

require 'release_tools/time_util'

module ReleaseTools
  module Slack
    class CoordinatedPipelineNotification
      include ::SemanticLogger::Loggable
      include ReleaseTools::TimeUtil
      include Utilities

      # deploy_version  - Deployer package
      # pipeline        - GitLab response pipeline object
      # environment     - gstg-ref, gstg-cny, gstg, gprd-cny, gprd
      def initialize(deploy_version:, pipeline:, environment:)
        @deploy_version = deploy_version
        @pipeline = pipeline
        @environment = environment
      end

      def execute
        logger.info('Sending slack notification', deploy_version: deploy_version, environment: environment, deployer_url: deployer_url)

        response = send_notification

        return response unless send_diffs_notification?

        send_threaded_diffs_notification(response['ts'])
      end

      private

      attr_reader :deploy_version, :pipeline, :environment

      def deployer_url
        pipeline&.web_url
      end

      def send_notification
        params = {
          channel: slack_channel[:id],
          message: fallback_text,
          blocks: slack_block
        }

        if thread_to_started_notification?
          params[:additional_options] = {
            thread_ts: started_notification_message['ts'],
            reply_broadcast: true
          }
        end

        ReleaseTools::Slack::Message.post(**params)
      end

      def slack_channel
        if environment == 'gstg-ref'
          { id: Slack::STAGING_REF_DEPLOYMENT, name: Slack::STAGING_REF_DEPLOYMENT_NAME }
        else
          { id: Slack::ANNOUNCEMENTS, name: Slack::ANNOUNCEMENTS_NAME }
        end
      end

      def thread_to_started_notification?
        # Thread "success" or "failed" notifications to the started notification.
        deployer_status != DEPLOYER_STATUS_STARTED && started_notification_message
      end

      def fallback_text
        "#{environment} #{deployer_status} #{deploy_version}"
      end

      def slack_block
        [
          {
            type: 'section',
            text: ReleaseTools::Slack::Webhook.mrkdwn(section_block)
          },
          {
            type: 'context',
            elements: context_elements
          }
        ]
      end

      def section_block
        [].tap do |text|
          text << environment_icon
          text << status_icon
          text << release_managers_mention if mention_release_managers?
          text << "*#{environment}*"
          text << deployer_status_link
          text << "`#{deploy_version}`"
        end.join(' ')
      end

      def deployer_status_link
        if pipeline
          "<#{deployer_url}|#{deployer_status}>"
        else
          "Downstream pipeline couldn't be found, check if `#{bridge_job_name}` job in <#{ENV.fetch('CI_PIPELINE_URL', nil)}|coordinated pipeline> has started."
        end
      end

      def bridge_job_name
        "deploy:#{environment}"
      end

      def context_elements
        [].tap do |elements|
          elements << clock_context_element
          elements << { type: 'mrkdwn', text: ":sentry: #{new_sentry_link}" }
          elements << { type: 'mrkdwn', text: ":timer_clock: #{wall_duration}" } if finished_or_failed?
        end
      end

      def new_sentry_link
        version = ReleaseTools::AutoDeploy::Version.new(deploy_version)

        "<https://new-sentry.gitlab.net/gitlab/gitlabcom/releases/#{version.rails_sha}/|View Sentry>"
      end

      def wall_duration
        duration(current_time - start_time).first
      end

      def start_time
        Time.parse(pipeline.created_at)
      end

      def finished_or_failed?
        [DEPLOYER_STATUS_SUCCESS, DEPLOYER_STATUS_FAILED].include?(deployer_status)
      end

      def send_diffs_notification?
        deployer_status == DEPLOYER_STATUS_STARTED
      end

      def send_threaded_diffs_notification(thread_ts)
        args = {
          deploy_version: deploy_version,
          environment: environment,
          thread_ts: thread_ts,
          channel: slack_channel[:id]
        }

        ReleaseTools::Slack::CoordinatedPipelineDiffsNotification.new(**args).execute
      end

      def started_notification_message
        return @started_notification_message if @started_notification_message

        results = ReleaseTools::Slack::Search.query(
          "#{environment} #{DEPLOYER_STATUS_STARTED} #{deploy_version}",
          channel: slack_channel[:name]
        )

        deployment_started_notification_regex = /\*#{environment}\* <[^|]+\|#{DEPLOYER_STATUS_STARTED}> `#{deploy_version}`$/

        @started_notification_message =
          results
            .detect do |m|
              m.dig('blocks', 0, 'text', 'text') =~ deployment_started_notification_regex
            end

        if @started_notification_message
          logger.info('Found deployment started notification', slack_msg: @started_notification_message['permalink'], deploy_version: deploy_version, environment: environment, deployer_url: deployer_url)
        else
          logger.warn('Could not find deployment started notification', deploy_version: deploy_version, environment: environment, deployer_url: deployer_url)
        end

        @started_notification_message
      end
    end
  end
end
