#!/usr/bin/env ruby
# frozen_string_literal: true

if !ENV["LD_PRELOAD"]&.include?('mwrap')
  # use malloc from libc that interacts better with mwrap
  ENV['RAILS_ENV'] = 'production'
  ENV["LD_PRELOAD"] = ""
  ENV["MALLOC_ARENA_MAX"] = "2"
  exec "mwrap #{__FILE__}"
end

require 'mwrap'
require 'webrick'

require File.expand_path("../../config/environment", __FILE__)

def render_table(array)
  buffer = +""

  width = array[0].map { |k| k.to_s.length }
  cols = array[0].length

  array.each do |row|
    row.each_with_index do |val, i|
      width[i] = [width[i].to_i, val.to_s.length].max
    end
  end

  array[0].each_with_index do |col, i|
    buffer << col.to_s.ljust(width[i], ' ')
    if i == cols - 1
      buffer << "\n"
    else
      buffer << ' | '
    end
  end

  buffer << ("-" * (width.sum + width.length))
  buffer << "\n"

  array.drop(1).each do |row|
    row.each_with_index do |val, i|
      buffer << val.to_s.ljust(width[i], ' ')
      if i == cols - 1
        buffer << "\n"
      else
        buffer << ' | '
      end
    end
  end

  buffer
end

def rss
  `ps -o rss= -p #{Process.pid}`.chomp.to_i
end

def mwrap_log
  report = +""

  Mwrap.quiet do
    report << "Generation: #{GC.count} RSS kb: #{rss} Accounted Mem kb: #{(Mwrap.total_bytes_allocated - Mwrap.total_bytes_freed) / 1024}\n"
    report << "Allocated bytes: #{Mwrap.total_bytes_allocated} Freed bytes: #{Mwrap.total_bytes_freed}\n"
    stat = GC.stat
    stat.each do |k, v|
      report << "#{k}: #{v}\n"
    end
    report << "\n"

    table = []
    Mwrap.each(200) do |loc, total, allocations, frees, age_sum, max_life|
      table << [total, allocations, frees, frees == 0 ? -1 : (age_sum / frees.to_f).round(2), max_life, loc]
    end

    table.sort! { |a, b| b[1] - b[2] <=> a[1] - a[2] }

    table.prepend(["total", "allocations", "frees", "mean_life", "max_life", "location"])

    report << render_table(table)
  end

  report
end

Thread.new do
  begin
    puts "Starting Logging Thread"
    path = "/tmp/mwrap_#{Process.pid}"
    `mkdir -p #{path}`

    while true
      log = mwrap_log
      f = "#{path}/log_#{Time.now.strftime("%Y_%m_%d_%H%M%S")}"
      File.write(f, log)
      puts "Wrote #{f}"
      sleep 60 * 60
    end
  rescue => e
    STDERR.puts "ERROR crashed logger #{e}"
    STDERR.puts e.backtrace
  end
end

Thread.new do
  puts "Starting WEBrick on port 9874"
  server = WEBrick::HTTPServer.new(Port: 9874)
  server.mount_proc '/' do |req, res|
    res['ContentType'] = 'text/plain; charset=utf-8'
    res.body = mwrap_log
    res.status = 200
  end
  begin
    server.start
  rescue => e
    STDERR.puts "Failed to start start server #{e}"
  end
end

require 'sidekiq/cli'

begin
  #Sidekiq::Logging.logger = nil
  cli = Sidekiq::CLI.instance

  options = ["-c", GlobalSetting.sidekiq_workers.to_s]

  [['critical', 8], ['default', 4], ['low', 2], ['ultra_low', 1]].each do |queue_name, weight|
    custom_queue_hostname = ENV["UNICORN_SIDEKIQ_#{queue_name.upcase}_QUEUE_HOSTNAME"]

    if !custom_queue_hostname || custom_queue_hostname.split(',').include?(Discourse.os_hostname)
      options << "-q"
      options << "#{queue_name},#{weight}"
    end
  end

  cli.parse(options)
  load Rails.root + "config/initializers/100-sidekiq.rb"
  cli.run
rescue => e
  STDERR.puts e.message
  STDERR.puts e.backtrace.join("\n")
  exit 1
end