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

require "pathname"
require "open3"

RAILS_ROOT = File.expand_path("../../", Pathname.new(__FILE__).realpath)
PORT = ENV["UNICORN_PORT"] ||= "3000"
HOSTNAME = ENV["DISCOURSE_HOSTNAME"] ||= "127.0.0.1"
CUSTOM_ARGS = %w[--try --test --build --server --unicorn -u --forward-host]
PROXY =
  if ARGV.include?("--try")
    "https://try.discourse.org"
  else
    "http://#{HOSTNAME}:#{PORT}"
  end

def process_running?(pid)
  !!Process.kill(0, pid)
rescue Errno::ESRCH
  false
end

command =
  if ARGV.include?("--test")
    "test"
  elsif ARGV.include?("--build")
    "build"
  else
    "server"
  end

class String
  def cyan
    "\e[36m#{self}\e[0m"
  end

  def red
    "\033[31m#{self}\e[0m"
  end
end

if ARGV.include?("-h") || ARGV.include?("--help")
  puts "ember-cli OPTIONS"
  puts "#{"--try".cyan} To proxy try.discourse.org"
  puts "#{"--test".cyan} To run the test suite"
  puts "#{"--server, -u".cyan} To run a server as well"
  puts "The rest of the arguments are passed to ember server per:", ""
  exec "pnpm ember #{command} --help"
end

args = ["--dir=frontend/discourse", "ember", command] + (ARGV - CUSTOM_ARGS)

if !args.include?("test") && !args.include?("build") && !args.include?("--proxy")
  args << "--proxy"
  args << PROXY
end

node_modules_outdated =
  begin
    File.read("node_modules/.pnpm/lock.yaml") != File.read("pnpm-lock.yaml")
  rescue Errno::ENOENT
    true
  end

if node_modules_outdated
  puts "[bin/ember-cli] Detected outdated or missing node_modules. Running pnpm install..."

  if !system "pnpm", "--dir=#{RAILS_ROOT}", "install"
    if !system("command -v pnpm >/dev/null;")
      abort "pnpm is not installed. run `npm install -g pnpm`"
    end
    exit 1
  end
end

pnpm_env = {
  "TERM" => "dumb", # simple output from ember-cli, so we can parse/forward it more easily
}
pnpm_env["FORWARD_HOST"] = "true" if ARGV.include?("--forward-host")

if ARGV.include?("-u") || ARGV.include?("--server") || ARGV.include?("--unicorn")
  server_env = { "DISCOURSE_PORT" => ENV["DISCOURSE_PORT"] || "4200" }

  if command == "server" && ENV["CODESPACE_NAME"]
    server_env.merge!(
      {
        "DISCOURSE_PORT" => "443",
        "DISCOURSE_FORCE_HTTPS" => "1",
        "DISCOURSE_FORCE_HOSTNAME" =>
          "#{ENV["CODESPACE_NAME"]}-4200.#{ENV["GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN"]}",
      },
    )
  end

  server_pid = spawn(server_env, "#{__dir__}/pitchfork")
  ember_cli_pid = nil
  sigint_received = false

  Thread.new do
    Open3.popen2e(pnpm_env, "pnpm", *args.to_a.flatten) do |i, oe, t|
      ember_cli_pid = t.pid
      puts "Ember CLI running on PID: #{ember_cli_pid}"
      oe.each do |line|
        if line.include?("\e[32m200\e") || line.include?("\e[36m304\e[0m") ||
             line.include?("POST /message-bus")
          # skip 200s and 304s and message bus
        else
          puts "[ember-cli] #{line}"
        end
      end
    end
    # Only send TERM to the server if ember exited on its own (not from
    # Ctrl+C). When the user presses Ctrl+C, all processes in the foreground
    # group already received INT directly from the terminal. Sending an
    # additional TERM races with the server's hard shutdown and can cause it
    # to fall back to a slow graceful shutdown, making the process appear
    # stuck for up to 60 seconds.
    if !sigint_received && process_running?(server_pid)
      puts "[bin/ember-cli] ember-cli process stopped. Terminating server."
      Process.kill("TERM", server_pid)
    end
  end

  int_count = 0
  trap("INT") do
    sigint_received = true
    int_count += 1
    case int_count
    when 1
      # Swallowed to give children time to handle it
    when 2
      puts "\n[bin/ember-cli] Interrupt again to force shutdown..."
    else
      puts "\n[bin/ember-cli] Forcing shutdown..."
      begin
        Process.kill("KILL", server_pid)
      rescue StandardError
        nil
      end
      if ember_cli_pid
        begin
          Process.kill("KILL", ember_cli_pid)
        rescue StandardError
          nil
        end
      end
      exit!(1)
    end
  end

  Process.wait(server_pid)

  if ember_cli_pid && process_running?(ember_cli_pid)
    puts "[bin/ember-cli] server process stopped. Terminating ember-cli."
    Process.kill("TERM", ember_cli_pid)
  end
else
  exec(pnpm_env, "pnpm", *args.to_a.flatten)
end
