# frozen_string_literal: true

module GitlabQuality
  module TestTooling
    module TestResult
      class JsonTestResult < BaseTestResult
        PRIVATE_TOKEN_REGEX = /(private_token=)[\w-]+/

        OTHER_TESTS_MAX_DURATION = 45.40 # seconds

        TestLevelSpecification = Struct.new(:regex, :max_duration)

        TEST_LEVEL_SPECIFICATIONS = [
          TestLevelSpecification.new(%r{spec/features/}, 50.13),
          TestLevelSpecification.new(%r{spec/(controllers|requests)/}, 19.20),
          TestLevelSpecification.new(%r{spec/lib/}, 27.12),
          TestLevelSpecification.new(%r{qa/specs/features/}, 240)
        ].freeze

        def name
          report.fetch('full_description')
        end

        def file
          report.fetch('file_path').delete_prefix('./')
        end

        def status
          report.fetch('status')
        end

        def skipped?
          status == 'pending'
        end

        def failed?
          status == 'failed'
        end

        def ci_job_url
          report.fetch('ci_job_url', '')
        end

        def testcase
          report.fetch('testcase', '')
        end

        def testcase=(new_testcase)
          report['testcase'] = new_testcase
        end

        def failure_issue
          report['failure_issue']
        end

        def failure_issue=(new_failure_issue)
          report['failure_issue'] = new_failure_issue
        end

        def quarantine?
          # The value for 'quarantine' could be nil, a hash, a string,
          # or true (if the test just has the :quarantine tag)
          # But any non-nil or false value should means the test is in quarantine
          !!quarantine
        end

        def quarantine_type
          quarantine['type'] if quarantine?
        end

        def quarantine_issue
          quarantine['issue'] if quarantine?
        end

        def screenshot?
          !!screenshot
        end

        def screenshot_image
          screenshot['image'] if screenshot?
        end

        def product_group
          report['product_group'].to_s
        end

        def product_group?
          product_group != ''
        end

        def feature_category
          report['feature_category']
        end

        def category
          report['category']
        end

        def run_time
          report['run_time'].to_f.round(2)
        end

        def example_id
          report['id']
        end

        def line_number
          report['line_number']
        end

        def level
          report['level']
        end

        def ci_job_id
          report['ci_job_url'].split('/').last
        end

        def failures # rubocop:disable Metrics/AbcSize
          @failures ||=
            report.fetch('exceptions', []).filter_map do |exception|
              backtrace = exception['backtrace']
              next unless backtrace.respond_to?(:rindex)

              spec_file_first_index = backtrace.rindex do |line|
                line.include?(File.basename(report['file_path']))
              end

              message = redact_private_token(exception['message'])
              message_lines = Array(exception['message_lines']).map { |line| redact_private_token(line) }

              {
                'message' => "#{exception['class']}: #{message}",
                'message_lines' => message_lines,
                'stacktrace' => "#{format_message_lines(message_lines)}\n#{backtrace.slice(0..spec_file_first_index).join("\n")}",
                'correlation_id' => exception['correlation_id']
              }
            end
        end

        def allowed_to_be_slow?
          !!report['allowed_to_be_slow']
        end

        def slow_test?
          !allowed_to_be_slow? && run_time > max_duration_for_test
        end

        def max_duration_for_test
          test_level_specification = TEST_LEVEL_SPECIFICATIONS.find do |test_level_specification|
            example_id =~ test_level_specification.regex
          end
          return OTHER_TESTS_MAX_DURATION unless test_level_specification

          test_level_specification.max_duration
        end

        def test_file_link
          path_prefix = file.start_with?('qa/') ? 'qa/' : ''

          "[`#{path_prefix}#{file}#L#{line_number}`](#{Runtime::Env.file_base_url}#{path_prefix}#{file}#L#{line_number})"
        end

        private

        def quarantine
          report.fetch('quarantine', nil)
        end

        def screenshot
          report.fetch('screenshot', nil)
        end

        def format_message_lines(message_lines)
          message_lines.is_a?(Array) ? message_lines.join("\n") : message_lines
        end

        def redact_private_token(text)
          text.gsub(PRIVATE_TOKEN_REGEX, '********')
        end
      end
    end
  end
end