#!/usr/bin/env ruby

$LOAD_PATH.unshift File.expand_path('../lib', __dir__)

require 'fileutils'
require 'gpt_common'
require 'json'
require 'naturally'
require 'optimist'
require 'run_k6'
require 'table_print'
require 'time'
require 'result_writer'

# Get default results dir path from the current file
results_dir = Pathname.new(File.expand_path('../results', __dir__)).relative_path_from(Dir.pwd)

@opts = Optimist.options do
  banner "Usage: ci-report-results-wiki [options]"
  banner "\nReports GitLab Performance Tool test results to a Wiki. Designed for use in GitLab CI."
  banner "\nOptions:"
  opt :help, 'Show help message'
  opt :results_path, "Path of k6 test results files to report on. Can be a directory that will be searched recursively or a direct filepath.", type: :string, default: results_dir.to_s
  opt :page_title, "Title to use for wiki page", type: :string
  opt :api_url, "GitLab wiki API URL", type: :string, required: true
  opt :dry_run, "Only generate Wiki page locally and don't post for testing purposes.", type: :flag
  banner "\nEnvironment Variable(s):"
  banner "  CI_PROJECT_ACCESS_TOKEN   A valid GitLab Personal Access Token that has access to the intended project where the wiki page will be posted. The token should come from a User that has admin access for the project(s) and have all permissions. (Default: nil)"
end

raise 'Environment Variable CI_PROJECT_ACCESS_TOKEN must be set to proceed. See command help for more info' unless ENV['CI_PROJECT_ACCESS_TOKEN'] || @opts[:dry_run]
raise 'Page Title must be specified' unless @opts[:page_title] || @opts[:dry_run]

def get_comparison_data(results)
  results_comparison_data = {}
  results_comparison_versions = []
  ttfb_overall_target = (ENV['GPT_TARGET_TTFB']&.dup || 200).to_f

  results.each do |result|
    results_comparison_versions |= [result["version"].tr('.', '-')]
  end

  results.each_with_index do |result, result_num|
    result['test_results'].each do |test_result|
      results_comparison_data[test_result["name"]] ||= results_comparison_versions.to_h { |version| [version, "-"] }
      results_comparison_data[test_result["name"]]['target'] ||= "#{test_result['ttfb_p90_threshold']}ms"

      test_result_score = ((test_result['ttfb_p90'].to_f / ttfb_overall_target) * 100)
      last_test_result = results[result_num - 1]['test_results'].find { |res| res['name'] == test_result["name"] } unless result_num.zero?
      if last_test_result&.dig('ttfb_p90')
        last_test_result_score = ((last_test_result['ttfb_p90'].to_f / ttfb_overall_target) * 100)
        last_test_result_score_diff = (test_result_score - last_test_result_score).round(2)
        last_test_result_score_diff_str = last_test_result_score_diff.positive? ? "_{-+#{last_test_result_score_diff}%-}_" : "**{+#{last_test_result_score_diff}%+}**"

        results_comparison_data[test_result["name"]][result["version"].tr('.', '-')] = "#{test_result['ttfb_p90']}ms (#{last_test_result_score_diff_str})"
      else
        results_comparison_data[test_result["name"]][result["version"].tr('.', '-')] = test_result['ttfb_p90'] ? "#{test_result['ttfb_p90']}ms (-)" : '-'
      end
    end
  end

  results_comparison_data
end

def generate_comparison_table(results_comparison_data)
  results_comparison_table = results_comparison_data.sort.map { |name, results| { 'name' => name, 'target' => results['target'] }.merge(results) }

  tp.set(:max_width, 60)
  TablePrint::Printer.table_print(results_comparison_table)
end

def fetch_csv_result
  csv_results_file = @opts[:results_path].include?(".csv") ? Dir.glob(@opts[:results_path]) : (Dir.glob("#{@opts[:results_path]}/**/*_results.csv") - Dir.glob("#{@opts[:results_path]}/**/*_failed_results.csv"))
  csv_results = File.read(csv_results_file.first)
  <<~DOC
    ### Test Results in CSV format
    Test Results in CSV format can be downloaded and compared directly with the CSV output from customer self-managed GitLab instance tests.
    <details><summary>Click to expand</summary>

    ```txt
    #{csv_results}
    ```
    </details>
  DOC
end

results_files = @opts[:results_path].include?(".json") ? Dir.glob(@opts[:results_path]) : (Dir.glob("#{@opts[:results_path]}/**/*_results.json") - Dir.glob("#{@opts[:results_path]}/**/*_failed_results.json"))
results_unsorted = results_files.map { |results_file| JSON.parse(File.read(results_file)) }
results_list = Naturally.sort_by(results_unsorted) { |results| results['version'] }
raise "\nNo results found in specified path(s):\n#{@opts[:results_path]}\nExiting..." if results_list.empty?

results_comparison_contents = results_list.length > 1 ? "[[_TOC_]]\n" : ""
if results_list.length > 1
  results_comparison_data = get_comparison_data(results_list)
  results_comparison_table = generate_comparison_table(results_comparison_data)
  results_comparison_contents << "## comparisons\n#{results_comparison_table}\n\nPercentages shown above are [percentage point changes](https://en.wikipedia.org/wiki/Percentage_point) calculated against the ideal target of 200ms unless specified otherwise.\n"
end

results_list.each do |results|
  results_sections = ResultWriter::Txt::Sections.new.generate(results)
  results_summary, results_table, results_footer = results_sections.values_at(:summary, :table, :footer)

  results_comparison_contents << "\n## #{results['name'].capitalize} - #{results['version']}\n" unless results_list.length == 1
  results_comparison_contents << "### Test Links" if ENV['CI_PIPELINE_URL'] || ENV['ENVIRONMENT_GRAFANA_DASHBOARD_URL'] || ENV['TEST_RESULTS_GRAFANA_DASHBOARD_URL']
  results_comparison_contents << "\n* [Pipeline](#{ENV['CI_PIPELINE_URL']})" if ENV['CI_PIPELINE_URL']
  results_comparison_contents << "\n* [Metrics Dashboard](#{ENV['ENVIRONMENT_GRAFANA_DASHBOARD_URL']}?from=#{results['time']['start_epoch']}&to=#{results['time']['end_epoch']})" if ENV['ENVIRONMENT_GRAFANA_DASHBOARD_URL']
  results_comparison_contents << "\n* [Results History Dashboard](#{ENV['TEST_RESULTS_GRAFANA_DASHBOARD_URL']})" if ENV['TEST_RESULTS_GRAFANA_DASHBOARD_URL']
  results_comparison_contents << "\n* Grafana dashboard login credentials are stored in `Engineering` 1Password vault under `Performance Grafana dashboard credentials`" if ENV['ENVIRONMENT_GRAFANA_DASHBOARD_URL'] || ENV['TEST_RESULTS_GRAFANA_DASHBOARD_URL']
  results_comparison_contents << "\n### Test Results"
  results_comparison_contents << "\n#{results_summary}\n\n#{results_table}\n"
  results_comparison_contents << "\n#{results_footer}\n" unless results_footer.empty?
  # Remove any terminal ANSI codes
  results_comparison_contents.gsub!(/\e\[([;\d]+)?m/, '')
end

# Colored results highlight for wiki
results_comparison_contents.gsub!("Passed", '**{+Passed+}** ')
results_comparison_contents.gsub!("FAILED", '**{-FAILED-}** ')

results_comparison_contents << "\n#{fetch_csv_result}" if results_list.length == 1

results_comparison_file = File.join(@opts[:results_path], "results_comparison.txt")
puts "Saving results to #{results_comparison_file}"
File.write(results_comparison_file, results_comparison_contents)

exit if @opts[:dry_run]

puts "\nPosting results to Wiki page '#{@opts[:page_title]}'"
headers = { 'PRIVATE-TOKEN': ENV['CI_PROJECT_ACCESS_TOKEN'] }

create_params = {
  title: @opts[:page_title],
  content: "Autogenerated by GitLab Performance Tool"
}
GPTCommon.make_http_request(method: 'post', url: @opts[:api_url], params: create_params, headers:, fail_on_error: false)

edit_params = {
  title: @opts[:page_title],
  content: results_comparison_contents
}
GPTCommon.make_http_request(method: 'put', url: "#{@opts[:api_url]}/#{@opts[:page_title].gsub('/', '%2F')}", params: edit_params, headers:)
puts "\nResults posted!"
