require "erb"
require "forwardable"
require "honeybadger/cli/main"
require "honeybadger/cli/helpers"
require "honeybadger/util/http"
require "honeybadger/util/stats"
require "open3"
require "ostruct"
require "thor/shell"

module Honeybadger
  module CLI
    class Exec
      extend Forwardable
      include Helpers::BackendCmd

      FAILED_TEMPLATE = <<~MSG
        Honeybadger detected failure or error output for the command:
        `<%= args.join(' ') %>`
        
        PROCESS ID: <%= pid %>
        
        RESULT CODE: <%= code %>
        
        ERROR OUTPUT:
        <%= stderr %>
        
        STANDARD OUTPUT:
        <%= stdout %>
      MSG

      NO_EXEC_TEMPLATE = <<~MSG
        Honeybadger failed to execute the following command:
        `<%= args.join(' ') %>`
        
        The command was not executable. Try adjusting permissions on the file.
      MSG

      NOT_FOUND_TEMPLATE = <<~MSG
        Honeybadger failed to execute the following command:
        `<%= args.join(' ') %>`
        
        The command was not found. Make sure it exists in your PATH.
      MSG

      def initialize(options, args, config)
        @options = options
        @args = args
        @config = config
        @shell = ::Thor::Base.shell.new
      end

      def run
        result = exec_cmd
        return if result.success

        executable = args.first.to_s[/\S+/]
        payload = {
          api_key: config.get(:api_key),
          notifier: NOTIFIER,
          error: {
            class: "honeybadger exec error",
            message: result.msg
          },
          request: {
            component: executable,
            context: {
              command: args.join(" "),
              code: result.code,
              pid: result.pid,
              pwd: Dir.pwd,
              path: ENV["PATH"]
            }
          },
          server: {
            project_root: Dir.pwd,
            environment_name: config.get(:env),
            time: Time.now,
            stats: Util::Stats.all
          }
        }

        begin
          response = config.backend.notify(:notices, payload)
        rescue
          say(result.msg)
          raise
        end

        if !response.success?
          say(result.msg)
          say(error_message(response), :red)
          exit(1)
        end

        unless quiet?
          say(result.msg)
          say("\nSuccessfully notified Honeybadger")
        end

        exit(0)
      end

      private

      attr_reader :options, :args, :config

      def_delegator :@shell, :say

      def quiet?
        !!options[:quiet]
      end

      def exec_cmd
        stdout, stderr, status = Open3.capture3(args.join(" "))

        success = status.success? && stderr =~ BLANK
        pid = status.pid
        code = status.to_i
        msg = ERB.new(FAILED_TEMPLATE).result(binding) unless success

        OpenStruct.new(
          msg: msg,
          pid: pid,
          code: code,
          stdout: stdout,
          stderr: stderr,
          success: success
        )
      rescue Errno::EACCES, Errno::ENOEXEC
        OpenStruct.new(
          msg: ERB.new(NO_EXEC_TEMPLATE).result(binding),
          code: 126
        )
      rescue Errno::ENOENT
        OpenStruct.new(
          msg: ERB.new(NOT_FOUND_TEMPLATE).result(binding),
          code: 127
        )
      end
    end
  end
end
