require 'benchmark'

users_count = (ENV['USERS_COUNT'] || '10000').to_i
iterations = (ENV['ITER'] || '10').to_i
mentions_count = (ENV['MENTIONS_COUNT'] || '100').to_i

Setting.text_formatting = 'common_mark'
Setting.cache_formatted_text = 0

puts "ruby=#{RUBY_VERSION} db=#{ActiveRecord::Base.connection.adapter_name} users=#{users_count} mentions_per_text=#{mentions_count} iter=#{iterations}"

class SimpleHelper
  include ApplicationHelper
  include ActionView::Helpers::TagHelper
  include ActionView::Helpers::AssetTagHelper
  include ActionView::Helpers::UrlHelper
  include Rails.application.routes.url_helpers

  def controller
    nil
  end

  def current_menu_item
    nil
  end
end

helper = SimpleHelper.new

ActiveRecord::Base.transaction do
  puts "Inserting users..."
  total_inserted = 0

  users_count.times.each_slice(10000) do |slice|
    users_attrs = slice.map do |i|
      {
        login: "bench_user_#{i}",
        firstname: "Bench",
        lastname: "User #{i}",
        status: Principal::STATUS_ACTIVE,
        type: 'User',
        language: 'en',
        created_on: Time.now,
        updated_on: Time.now
      }
    end
    User.insert_all(users_attrs)
    total_inserted += users_attrs.size
    print "."
  end
  puts "\nUsers inserted: #{total_inserted}"

  sample_users = User.where("login LIKE 'bench_user_%'").limit(mentions_count).to_a

  if sample_users.length < mentions_count
    puts "Warning: Only #{sample_users.length} users generated"
  end

  project = Project.find(1)
  role = Role.givable.first

  puts "Assigning sample users to project..."
  # Only add the sampled users to the project for speed
  member_attrs = sample_users.map do |u|
    {
      user_id: u.id,
      project_id: project.id,
      created_on: Time.now
    }
  end

  Member.insert_all(member_attrs) if member_attrs.any?

  # Also need member_roles
  members = Member.where(user_id: sample_users.map(&:id)).pluck(:id)
  member_role_attrs = members.map do |member_id|
    {
      member_id: member_id,
      role_id: role.id
    }
  end

  MemberRole.insert_all(member_role_attrs) if member_role_attrs.any?

  text_login = sample_users.map { |u| "@#{u.login}" }.join(" ")
  text_user_link = sample_users.map { |u| "user##{u.id}" }.join(" ")

  # Worst case: mentions that look valid but don't exist in the DB,
  # forcing a sequential scan across all rows
  text_invalid_login = (1..mentions_count).map { |i| "@missing_user_#{i}" }.join(" ")

  prefix = "Here are some mentions: "
  text_login = prefix + text_login
  text_user_link = prefix + text_user_link
  text_invalid_login = prefix + text_invalid_login

  # Warm up
  helper.textilizable(text_login, project: project)
  helper.textilizable(text_user_link, project: project)
  helper.textilizable(text_invalid_login, project: project)

  puts "\nBenchmarking @login..."
  bm_login = Benchmark.realtime do
    ApplicationRecord.uncached do
      iterations.times { helper.textilizable(text_login, project: project) }
    end
  end
  puts "login_ms=#{(bm_login*1000).round(2)} avg_login=#{(bm_login*1000/iterations).round(2)}"

  puts "\nBenchmarking user#id..."
  bm_user_link = Benchmark.realtime do
    ApplicationRecord.uncached do
      iterations.times { helper.textilizable(text_user_link, project: project) }
    end
  end
  puts "user_link_ms=#{(bm_user_link*1000).round(2)} avg_user_link=#{(bm_user_link*1000/iterations).round(2)}"

  puts "\nBenchmarking @login (worst-case non-existent, forces full scan)..."
  bm_invalid_login = Benchmark.realtime do
    ApplicationRecord.uncached do
      iterations.times { helper.textilizable(text_invalid_login, project: project) }
    end
  end
  puts "invalid_login_ms=#{(bm_invalid_login*1000).round(2)} avg_invalid_login=#{(bm_invalid_login*1000/iterations).round(2)}"

  raise ActiveRecord::Rollback
end
