# frozen_string_literal: true

module ReleaseTools
  module Tasks
    module Components
      module Updater
        TARGET_PROJECT = Services::UpdateComponentService::TARGET_PROJECT
        ENV_TOKEN = 'GITLAB_BOT_PRODUCTION_TOKEN'

        # Attempt merge when MR is not_approved since the AutoMergeService approves the MR before calling the merge API.
        MERGE_STATUS_CAN_ATTEMPT_MERGE = %w[mergeable unchecked not_approved].freeze

        def initialize(token = nil)
          # Approving the merge request can't be done by the author, so for
          # approving and auto-merging we use a GitLab project token instead
          # of the release-tools bot.
          @gitlab_bot_token = token || ENV.fetch(ENV_TOKEN)
        end

        def execute
          if !changed?
            logger.info("#{project.metadata_project_name} already up to date")
          elsif merge_request.exists?
            logger.info('Found existing merge request', merge_request: merge_request.url, mwps: merge_request.merge_when_pipeline_succeeds?, merge_status: merge_request.detailed_merge_status)

            return create_merge_request_and_set_auto_merge if !merge_request.merge_when_pipeline_succeeds? && attempt_merge?

            notify_stale_merge_request if merge_request.notifiable?
          else
            logger.info("Creating merge request to update #{project.metadata_project_name}")
            create_merge_request_and_set_auto_merge
          end
        end

        def changed?
          Services::UpdateComponentService.new(project, project.default_branch).changed?
        end

        def create_merge_request_and_set_auto_merge
          ensure_source_branch_exists
          create_or_show_merge_request(merge_request)

          logger.info("Updating #{project.metadata_project_name} version")
          commit = Services::UpdateComponentService
                     .new(project, source_branch, skip_ci: true)
                     .execute

          Services::AutoMergeService.new(merge_request, token: @gitlab_bot_token, commit: commit).execute
        end

        def ensure_source_branch_exists
          logger.info('Making sure source branch exists', source_branch: source_branch)
          return if SharedStatus.dry_run?

          GitlabClient.find_or_create_branch(source_branch, TARGET_PROJECT.default_branch, TARGET_PROJECT)
        end

        def notify_stale_merge_request
          logger.warn('Existing merge request is stale, issuing warning')
          return if SharedStatus.dry_run?

          send_slack_notification
          merge_request.mark_as_stale
        end

        def attempt_merge?
          MERGE_STATUS_CAN_ATTEMPT_MERGE.include?(merge_request.detailed_merge_status)
        end

        def send_slack_notification
          raise NotImplementedError
        end

        def project
          raise NotImplementedError
        end

        def source_branch
          raise NotImplementedError
        end

        def merge_request
          raise NotImplementedError
        end
      end
    end
  end
end
