# frozen_string_literal: true

require 'optparse'

require 'pf2'
require 'pf2/reporter'

module Pf2
  class CLI
    def self.run(...)
      new.run(...)
    end

    def run(argv)
      argv = argv.dup
      program_name = File.basename($PROGRAM_NAME)

      subcommand = argv.shift
      case subcommand
      when 'report'
        subcommand_report(argv)
      when 'annotate'
        subcommand_annotate(argv)
      when 'serve'
        subcommand_serve(argv)
      when 'version'
        puts VERSION
        return 0
      when '--help'
        STDERR.puts <<~__EOS__
        Usage: #{program_name} COMMAND [options]

        Commands:
          report    Generate a report from a profile
          serve     Start an HTTP server alongside a target process
          version   Show version information
        __EOS__

        return 1
      else
        STDERR.puts "#{program_name}: Unknown subcommand '#{subcommand}'."
        STDERR.puts "See '#{program_name} --help'"
        return 1
      end
    end

    def subcommand_report(argv)
      options = {}
      option_parser = OptionParser.new do |opts|
        opts.banner = "Usage: pf2 report [options] COMMAND"
        opts.on('-h', '--help', 'Prints this help') do
          puts opts
          return 0
        end
        opts.on('-o', '--output FILE', 'Output file') do |path|
          options[:output_file] = path
        end
      end
      option_parser.parse!(argv)

      profile = Marshal.load(File.read(argv[0]))
      report = Pf2::Reporter::FirefoxProfilerSer2.new(profile).emit
      report = JSON.generate(report)

      if options[:output_file]
        File.write(options[:output_file], report)
      else
        puts report
      end

      return 0
    end

    def subcommand_annotate(argv)
      options = {}
      option_parser = OptionParser.new do |opts|
        opts.banner = "Usage: pf2 report [options] COMMAND"
        opts.on('-h', '--help', 'Prints this help') do
          puts opts
          return 0
        end
        opts.on('-d', '--source-directory DIR', 'Path to the source directory') do |dir|
          options[:source_directory] = dir
        end
      end
      option_parser.parse!(argv)

      profile = Marshal.load(File.binread(argv[0]))
      Pf2::Reporter::Annotate.new(profile, options[:source_directory] || '.').annotate

      return 0
    end

    def subcommand_serve(argv)
      options = {}
      option_parser = OptionParser.new do |opts|
        opts.banner = "Usage: pf2 serve [options] COMMAND"
        opts.on('-h', '--help', 'Prints this help') do
          puts opts
          return 0
        end
        opts.on('-b', '--bind ADDRESS', 'Address to bind') do |host|
          options[:serve_host] = host
        end
        opts.on('-p', '--port PORT', '') do |port|
          options[:serve_port] = port
        end
      end
      option_parser.parse!(argv)

      if argv.size == 0
        # No subcommand was specified
        STDERR.puts option_parser.help
        return 1
      end

      # Inject the profiler (pf2/serve) into the target process via RUBYOPT (-r).
      # This will have no effect if the target process is not Ruby.
      env = {
        'RUBYOPT' => '-rpf2/serve'
      }
      env['PF2_SERVE_HOST'] = options[:serve_host] if options[:serve_host]
      env['PF2_SERVE_PORT'] = options[:serve_port] if options[:serve_port]
      exec(env, *argv) # never returns if succesful

      return 1
    end
  end
end
