return unless ENV["CI"]

require "csv"
require "rspec/retry"
require "singleton"

class CSVFormatter
  RSpec::Core::Formatters.register self, :example_passed, :example_failed, :example_pending, :close
  HEADERS = ["Description", "File", "Status", "Start Date", "Start Time", "Run Time", "Exception",
             "Backtrace", "Retry #", "Suite Status", "Suite Run Time", "Travis URL", "Travis Branch"].freeze

  def initialize(_output)
    @rows = []
    @suite_start_time = Time.zone.now
    @suite_status = :passed
  end

  def example_pending(notification)
    description = notification.example.metadata[:full_description]
    file = notification.example.metadata[:location]
    status = :pending
    started_at = notification.example.metadata[:execution_result].started_at
    start_date = started_at.strftime("%m-%d-%Y")
    start_time = started_at.strftime("%H-%M-%S.%3N")
    run_time = notification.example.metadata[:execution_result].run_time.round(3)
    @rows << [description, file, status, start_date, start_time, run_time, nil, nil, nil]
  end

  def example_passed(notification)
    description = notification.example.metadata[:full_description]
    file = notification.example.metadata[:location]
    status = :passed
    started_at = notification.example.metadata[:execution_result].started_at
    start_date = started_at.strftime("%m-%d-%Y")
    start_time = started_at.strftime("%H-%M-%S.%3N")
    run_time = notification.example.metadata[:execution_result].run_time.round(3)
    retry_attempt = notification.example.metadata[:retry_attempts]
    @rows << [description, file, status, start_date, start_time, run_time, nil, nil, retry_attempt]
  end

  def example_failed(notification)
    @suite_status = :failed # If we're in here at all it means the entire run will count as failed
    description = notification.example.metadata[:full_description]
    file = notification.example.metadata[:location]
    status = :failed
    started_at = notification.example.metadata[:execution_result].started_at
    start_date = started_at.strftime("%m-%d-%Y")
    start_time = started_at.strftime("%H-%M-%S.%3N")
    run_time = notification.example.metadata[:execution_result].run_time.round(3)
    exception = notification.example.metadata[:execution_result].exception.inspect
    backtrace = notification.example.metadata[:execution_result].exception.backtrace
    backtrace = backtrace.join("   ") if backtrace
    retry_attempt = notification.example.metadata[:retry_attempts]
    @rows << [description, file, status, start_date, start_time, run_time, exception, backtrace, retry_attempt]
  end

  def close(_notification)
    csvs_dir = File.join(Dir.pwd, "tmp", "csvs")
    FileUtils.mkdir_p(csvs_dir)

    timestamp = Time.current.utc.iso8601.tr(":", "-")
    csv_filename = File.join(csvs_dir, "#{timestamp}.csv")

    suite_runtime = (Time.zone.now - @suite_start_time).round(3)

    with_headers = { write_headers: true, headers: HEADERS }
    CSV.open(csv_filename, "w", **with_headers) do |csv|
      (@rows + RSpecRetryFormatterHelper.instance.rows).each do |row|
        row += [@suite_status, suite_runtime, ENV.fetch("TRAVIS_BUILD_WEB_URL", nil), ENV.fetch("TRAVIS_BRANCH", nil)]
        csv << row
      end
    end
  end
end

# This singleton exists so that we can get retry data from the retry_callback in the CSVFormatter
class RSpecRetryFormatterHelper
  include Singleton
  attr_accessor :rows

  def initialize
    @rows = []
  end
end

RSpec.configure do |config|
  config.add_formatter(CSVFormatter)

  config.verbose_retry = true
  config.display_try_failure_messages = true
  config.around :each, :js do |ex|
    ex.run_with_retry retry: 3
  end

  # None of the rspec formatter hooks are invoked in the case of a retry, so the only way to get data
  # from retried tests is to store it in a singleton
  config.retry_callback = proc do |ex|
    description = ex.metadata[:full_description]
    file = ex.metadata[:location]
    status = :failed
    started_at = ex.metadata[:execution_result].started_at
    start_date = started_at.strftime("%m-%d-%Y")
    start_time = started_at.strftime("%H-%M-%S.%3N")
    run_time = (Time.zone.now - started_at).round(3)
    exception = ex.metadata[:retry_exceptions].last.inspect
    backtrace = ex.metadata[:retry_exceptions].last.backtrace
    backtrace = backtrace.join("   ") if backtrace
    retry_attempt = ex.metadata[:retry_attempts]

    RSpecRetryFormatterHelper.instance.rows << [description, file, status, start_date, start_time, run_time,
                                                exception, backtrace, retry_attempt]
  end
end
