#!/usr/bin/env ruby

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

require 'fileutils'
require 'gpt_common'
require 'http'
require 'json'
require 'optimist'
require 'pathname'
require 'run_k6'
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-slack [options]"
  banner "\nReports GitLab Performance Tool test results to Slack. 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 :test_name, "Name of test that's being reported on.", type: :string, required: true
  opt :test_result, "The result of the test. Must be either 'passed' or 'failed'.", type: :string, required: true
  opt :channel, "Slack channel to post results to.", type: :string, default: "gpt-performance-run"
  banner "\nEnvironment Variable(s):"
  banner "  ENVIRONMENT_NAME                    Name of environment. (Default: nil)"
  banner "  ENVIRONMENT_GRAFANA_DASHBOARD_URL   URL to environment's Grafana dashboard to show in Slack message. (Default: nil)"
  banner "  GPT_RESULTS_URL                     URL of Wiki page that also shows tests results to show in Slack message. (Default: nil)"
  banner "  GPT_KNOWN_ISSUES_URL                URL of known GitLab performance issues list. (Default: nil)"
  banner "  SLACK_BOT_TOKEN                     A valid Slack Token that belongs to a Bot that has permissions for the intended Slack instance. (Default: nil)"
end

raise 'Environment Variable SLACK_BOT_TOKEN must be set to proceed. See command help for more info' unless ENV['SLACK_BOT_TOKEN']
raise 'Test Result must be either \'passed\' or \'failed\'. Exiting...' unless @opts[:test_result].match?(/passed|failed/)

def post_slack_message(message)
  url = "https://slack.com/api/chat.postMessage"

  body = {}
  body['token'] = ENV['SLACK_BOT_TOKEN']
  body['channel'] = @opts[:channel]
  body['text'] = message

  GPTCommon.make_http_request(method: 'post', url:, params: body, show_response: false)
end

def prepare_message_text(result, results_json = nil)
  environment_name = ENV['ENVIRONMENT_REPORT_NAME'].dup || (ENV['ENVIRONMENT_NAME']&.match?(/docker/i) ? "multiple GitLab versions" : ENV['ENVIRONMENT_NAME'].capitalize)
  message = "#{@opts[:test_name].upcase} tests against #{environment_name} have"

  summary = if ENV['CI_SLACK_IGNORE_RESULT'] == 'true'
              ":information_source: #{message} completed!"
            else
              result ? ":ci_passing: #{message} passed! :ci_passing:" : ":ci_failing: #{message} failed! :ci_failing:"
            end

  summary << "\n\n• <#{ENV['CI_PIPELINE_URL']}|Pipeline>" if ENV['CI_PIPELINE_URL']

  # Construct and add Grafana links(s) string
  if results_json && (ENV['ENVIRONMENT_GRAFANA_DASHBOARD_URL'] || ENV['ENVIRONMENT_GRAFANA_GRAPH_IMAGES_URL'])
    grafana_str = "\n• <#{ENV['ENVIRONMENT_GRAFANA_DASHBOARD_URL']}?from=#{results_json['time']['start_epoch']}&to=#{results_json['time']['end_epoch']}|Metrics Dashboard>" if ENV['ENVIRONMENT_GRAFANA_DASHBOARD_URL']
    grafana_str += " (<#{ENV['ENVIRONMENT_GRAFANA_GRAPH_IMAGES_URL']}?from=#{results_json['time']['start_epoch']}&to=#{results_json['time']['end_epoch']}&panelId=2&width=1000&height=750&timeout=180|CPU>, <#{ENV['ENVIRONMENT_GRAFANA_GRAPH_IMAGES_URL']}?from=#{results_json['time']['start_epoch']}&to=#{results_json['time']['end_epoch']}&panelId=17&width=1000&height=750&timeout=180|Memory>)" if ENV['ENVIRONMENT_GRAFANA_GRAPH_IMAGES_URL']

    summary << grafana_str
  end

  summary << "\n• <#{ENV['TEST_RESULTS_GRAFANA_DASHBOARD_URL']}|Results History Dashboard>" if ENV['TEST_RESULTS_GRAFANA_DASHBOARD_URL']
  summary << "\n• <#{ENV['GPT_RESULTS_URL']}|Results>" if ENV['GPT_RESULTS_URL']
  summary << "\n• <#{ENV['GPT_KNOWN_ISSUES_URL']}|Known Issues>" if ENV['GPT_KNOWN_ISSUES_URL']
  summary
end

def format_failed_results(failed_results_txt)
  return nil unless File.exist?(failed_results_txt)

  formatted_text = "```\n#{File.read(failed_results_txt)}\n```"

  return formatted_text unless formatted_text.length > 3500

  # Handle Slack text field limitation of 4000 characters
  pipeline_url = ENV['CI_PIPELINE_URL'] ? "<#{ENV['CI_PIPELINE_URL']}|CI Pipeline>" : "GPT results logs"
  truncated_text = formatted_text[0, 3200] # Take first 3200 chars to leave room for the message
  warning_message = ":boom: Multiple tests failed. This is a truncated list. Check #{pipeline_url} for complete details.\n"

  "#{warning_message}\n#{truncated_text}...\n[truncated]\n```"
end

# Check if we have test results files
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_file = results_files.length == 1 ? results_files.first : nil

failed_results_txt = Dir.glob("#{@opts[:results_path]}/**/*_failed_results.txt").first
message = nil

if results_file && File.extname(results_file) == '.json'
  puts "Posting #{@opts[:test_result]} test result summary with formatted results to Slack"

  results_json = JSON.parse(File.read(results_file))
  result = @opts[:test_result] == "passed" || results_json['overall_result']
  message = prepare_message_text(result, results_json)

  # If we have failed results, format them as a code block and append to the message
  if failed_results_txt && !result
    formatted_results = format_failed_results(failed_results_txt)
    message << "\n\n*Failed Test Results:*\n#{formatted_results}" if formatted_results
  end
else
  puts "Posting result summary to Slack\n"

  result = @opts[:test_result] == "passed"
  message = prepare_message_text(result)
end

post_slack_message(message)
