# frozen_string_literal: true

module GitlabQuality
  module TestTooling
    module TestMeta
      module Processor
        class AddToQuarantineProcessor < MetaProcessor
          QUARANTINE_METADATA = <<~META
            ,
            %{indentation}quarantine: {
            %{indentation}  issue: '%{issue_url}',
            %{indentation}  type: %{quarantine_type}
            %{indentation}}%{suffix}
          META

          class << self
            # Execute the processor
            #
            # @param [Hash<String,String>] spec the spec to update
            # @option spec [String] :file_path the path to the spec file
            # @option spec [String] :stage the stage of the test
            # @option spec [String] :failure_issue the issue url of the failure
            # @option spec [String] :name the name of the example
            # @param [TestMetaUpdater] context instance of TestMetaUpdater
            def execute(spec, context) # rubocop:disable Metrics/AbcSize
              @context = context

              @file_path = spec["file_path"]
              devops_stage = spec["stage"]
              @failure_issue_url = spec["failure_issue"]
              @example_name = spec["name"]
              @issue_id = failure_issue_url.split('/').last # split url segment, last segment of path is the issue id
              @mr_title = format("%{prefix} %{example_name}", prefix: '[QUARANTINE]', example_name: example_name)
              @failure_issue = context.fetch_issue(iid: issue_id)

              return unless proceed_with_merge_request?

              @file_contents = context.get_file_contents(file_path)

              new_content, changed_line_no = add_quarantine_metadata

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

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

                Quarantine #{example_name}
              COMMIT_MESSAGE

              context.create_merge_request(mr_title, branch) do
                <<~MARKDOWN
                  ## What does this MR do?

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

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

                  ### E2E Test Failure issue(s)

                  #{failure_issue_url}

                  ### Check-list

                  - [ ] General code guidelines check-list
                    - [ ] [Code review guidelines](https://docs.gitlab.com/ee/development/code_review.html)
                    - [ ] [Style guides](https://docs.gitlab.com/ee/development/contributing/style_guides.html)
                  - [ ] Quarantine test check-list
                    - [ ] Follow the [Quarantining Tests guide](https://about.gitlab.com/handbook/engineering/infrastructure/test-platform/debugging-qa-test-failures/#quarantining-tests).
                    - [ ] Confirm the test has a [`quarantine:` tag with the specified quarantine type](https://about.gitlab.com/handbook/engineering/infrastructure/test-platform/debugging-qa-test-failures/#quarantined-test-types).
                    - [ ] Note if the test should be [quarantined for a specific environment](https://docs.gitlab.com/ee/development/testing_guide/end_to_end/execution_context_selection.html#quarantine-a-test-for-a-specific-environment).
                    - [ ] (Optionally) In case of an emergency (e.g. blocked deployments), consider adding labels to pick into auto-deploy (~"Pick into auto-deploy" ~"priority::1" ~"severity::1").
                  - [ ] To ensure a faster turnaround, ask in the `#quality_maintainers` Slack channel for someone to review and merge the merge request, rather than assigning it directly.

                  <!-- Base labels. -->
                  /label ~"Quality" ~"QA" ~"type::maintenance" ~"maintenance::pipelines"

                  <!--
                  Choose the stage that appears in the test path, e.g. ~"devops::create" for
                  `qa/specs/features/browser_ui/3_create/web_ide/add_file_template_spec.rb`.
                  -->
                  /label ~"devops::#{devops_stage}"

                  <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
            end

            # Performs post processing. Takes a list of MRs and posts them in a note on report_issue and Slack
            #
            # @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 quarantine the unstable tests:

                #{web_urls}
              ISSUE_NOTE

              context.post_message_on_slack(<<~MSG)
                *Action Required!* The following merge requests have been created to quarantine the unstable tests identified
                in the reliable test report: #{context.report_issue}

                #{web_urls}

                Maintainers are requested to review and merge. Thank you.
              MSG
            end

            private

            attr_reader :context, :file_path, :file_contents, :failure_issue_url, :example_name, :issue_id, :mr_title, :failure_issue

            # Checks if the failure issue is closed or if there is already an MR open
            #
            # @return [Boolean]
            def proceed_with_merge_request?
              if context.issue_is_closed?(failure_issue)
                puts "  Failure issue '#{failure_issue_url}' is closed. Will not proceed with creating MR."
                return false
              end

              open_mrs = context.existing_merge_requests(title: mr_title)
              if open_mrs.any?
                puts "  An open MR already exists for '#{example_name}':  #{open_mrs.first['web_url']}. Will not proceed with creating MR."
                return false
              end

              true
            end

            # Add quarantine 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_quarantine_metadata # rubocop:disable Metrics/AbcSize
              matched_lines = context.find_example_match_lines(file_contents, example_name)

              context.update_matched_line(matched_lines.last, file_contents.dup) do |line|
                indentation = context.indentation(line)

                if line.include?(',') && line.split.last != 'do'
                  line[line.index(',')] = format(QUARANTINE_METADATA.rstrip, issue_url: failure_issue_url, indentation: indentation, suffix: ',', quarantine_type: quarantine_type)
                else
                  line[line.rindex(' ')] = format(QUARANTINE_METADATA.rstrip, issue_url: failure_issue_url, indentation: indentation, suffix: ' ', quarantine_type: quarantine_type)
                end

                line
              end
            end

            # Returns the quarantine type based on the failure scoped label
            #
            # @return [String]
            def quarantine_type
              case context.issue_scoped_label(failure_issue, 'failure')&.split('::')&.last
              when 'new', 'investigating'
                ':investigating'
              when 'external-dependency'
                ':external_dependency'
              when 'broken-test'
                ':broken'
              when 'bug'
                ':bug'
              when 'flaky-test'
                ':flaky'
              when 'stale-test'
                ':stale'
              when 'test-environment'
                ':test_environment'
              else
                ':investigating'
              end
            end
          end
        end
      end
    end
  end
end