# frozen_string_literal: true

module GitlabQuality
  module TestTooling
    module TestMeta
      module Processor
        class AddToBlockingProcessor < MetaProcessor
          BLOCKING_METADATA = ", :blocking%{suffix}"

          class << self
            # Execute the processor
            #
            # @param [Hash] spec the spec to update
            # @param [TestMetaUpdater] context instance of TestMetaUpdater
            def execute(spec, context) # rubocop:disable Metrics/AbcSize
              @context = context
              @existing_mrs = nil
              @file_path = spec["file_path"]
              testcase = spec["testcase"]
              devops_stage = spec["stage"]
              product_group = spec["product_group"]
              @example_name = spec["name"]
              @mr_title = format("%{prefix} %{example_name}", prefix: '[PROMOTE TO BLOCKING]', example_name: example_name).truncate(72, omission: '')

              @file_contents = context.get_file_contents(file_path)

              new_content, @changed_line_no = add_blocking_metadata

              return unless proceed_with_merge_request?

              branch = context.create_branch("blocking-promotion-#{SecureRandom.hex(4)}", example_name, context.ref)

              context.commit_changes(branch, <<~COMMIT_MESSAGE, file_path, new_content)
                Promote end-to-end test to blocking

                #{"Promote to blocking: #{example_name}".truncate(72)}
              COMMIT_MESSAGE

              reviewer_id, assignee_handle = context.fetch_dri_id(product_group, devops_stage)

              gitlab_bot_user_id = context.user_id_for_username(Runtime::Env.gitlab_bot_username)

              merge_request = context.create_merge_request(mr_title, branch, gitlab_bot_user_id, [reviewer_id]) do
                <<~MARKDOWN
                  ## What does this MR do?

                  Promotes the test [`#{example_name}`](https://gitlab.com/#{context.project}/-/blob/#{context.ref}/#{file_path}#L#{changed_line_no + 1})
                  to the blocking bucket

                  This test was identified in the reliable e2e test report: #{context.report_issue}

                  [Testcase link](#{testcase})

                  [Spec metrics link](#{context.single_spec_metrics_link(example_name)})

                  /label ~"Quality" ~"QA" ~"type::maintenance"
                  /label ~"devops::#{devops_stage}"
                  #{context.label_from_product_group(product_group)}

                  <div align="center">
                  (This MR was automatically generated by [`gitlab_quality-test_tooling`](https://gitlab.com/gitlab-org/ruby/gems/gitlab_quality-test_tooling) at #{Time.now.utc})
                  </div>
                MARKDOWN
              end

              context.post_note_on_merge_request(<<~MARKDOWN, merge_request.iid)
                @#{assignee_handle} Please review this MR, approve and assign it to a maintainer.

                If you think this MR should not be merged, please close it and add a note of the reason to the blocking report: #{context.report_issue}
              MARKDOWN

              if merge_request
                context.add_processed_record({ file_path => changed_line_no })
                Runtime::Logger.info("  Created MR for promotion to blocking: #{merge_request.web_url}")
              end

              merge_request
            end

            # Performs post processing. Takes a list of MRs and posts them in a note on report_issue
            #
            # @param [Gitlab::ObjectifiedHash] merge_requests
            def post_process(merge_requests)
              web_urls = merge_requests.compact.map { |mr| "- #{mr.web_url}\n" }.join

              return if web_urls.empty?

              context.post_note_on_report_issue(<<~ISSUE_NOTE)
                The following merge requests have been created to promote stable specs to blocking:

                #{web_urls}
              ISSUE_NOTE
            end

            private

            attr_reader :context, :file_path, :file_contents, :example_name, :mr_title, :changed_line_no

            # Checks if there is already an MR open
            #
            # @return [Boolean]
            def proceed_with_merge_request? # rubocop:disable Metrics/AbcSize
              if changed_line_no.negative?
                Runtime::Logger.info("  No lines were changed in #{file_path}. Will not proceed with creating MR.")
                return false
              elsif context.record_processed?(file_path, changed_line_no)
                Runtime::Logger.info("  Record already processed for #{file_path}:#{changed_line_no}. Will not proceed with creating MR.")
                return false
              elsif existing_mrs&.any?
                Runtime::Logger.info("  An open MR already exists for '#{example_name}':  #{existing_mrs.first['web_url']}. Will not proceed with creating MR.")
                return false
              end

              true
            end

            # Add blocking metadata to the file content and replace it
            #
            # @return [Array<String, Integer>] first value holds the new content, the second value holds the line number of the test
            def add_blocking_metadata # rubocop:disable Metrics/AbcSize
              matched_lines = context.find_example_match_lines(file_contents, example_name)

              if matched_lines.any? { |line| line[0].include?(':blocking') }
                puts "Example '#{example_name}' is already blocking"
                return [file_contents, -1]
              end

              context.update_matched_line(matched_lines.last, file_contents.dup) do |line|
                if line.sub(DESCRIPTION_REGEX, '').include?(',')
                  line[line.index(',', end_of_description_index(line))] = format(BLOCKING_METADATA, suffix: ',')
                else
                  line[line.rindex(' ')] = format(BLOCKING_METADATA, suffix: ' ')
                end

                line
              end
            end
          end
        end
      end
    end
  end
end