require 'open3'
require 'shellwords'
require 'appmap/util'
require 'appmap/command_error'

module AppMap
  # Utilities for invoking the +@appland/appmap+ CLI.
  class NodeCLI
    APPMAP_JS = '@appland/appmap'

    attr_reader :verbose
    # Directory to scan for AppMaps.
    attr_accessor :appmap_dir

    def initialize(verbose: false, appmap_dir: AppMap::DEFAULT_APPMAP_DIR)
      @verbose = verbose
      @appmap_dir = appmap_dir

      detect_nodejs
    end

    def detect_nodejs
      do_fail('node', 'please install NodeJS') unless system('node --version 2>&1 > /dev/null')
      true
    end

    def index_appmaps
      command [ 'index', '--appmap-dir', appmap_dir ]
      true
    end

    def command(command, options = {})
      command.unshift << '--verbose' if verbose
      command.unshift APPMAP_JS
      command.unshift 'npx'

      warn command.join(' ') if verbose
      stdout, stderr, status = Open3.capture3({ 'NODE_OPTIONS' => '--trace-warnings' }, *command.map(&:shellescape), options)
      stdout_msg = stdout.split("\n").map {|line| "stdout: #{line}"}.join("\n") unless Util.blank?(stdout)
      stderr_msg = stderr.split("\n").map {|line| "stderr: #{line}"}.join("\n") unless Util.blank?(stderr)
      if verbose
        warn stdout_msg if stdout_msg
        warn stderr_msg if stderr_msg
      end
      unless status.exitstatus == 0
        raise CommandError.new(command, [ stdout_msg, stderr_msg ].compact.join("\n"))
      end
      [ stdout, stderr ]
    end

    protected

    def do_fail(command, msg)
      command = command.join(' ') if command.is_a?(Array)
      warn [ command, msg ].join('; ') if verbose
      raise CommandError.new(command, msg)
    end
  end
end
