# Copyright (c) 2015 Sqreen. All Rights Reserved.
# Please refer to our terms for more information: https://www.sqreen.io/terms.html
require 'sqreen/log'
require 'sqreen/events/remote_exception'

module Sqreen
  # Execute and sanitize remote commands
  class RemoteCommand
    KNOWN_COMMANDS = {
      :instrumentation_enable => :setup_instrumentation,
      :instrumentation_remove => :remove_instrumentation,
      :rules_reload => :reload_rules,
      :features_get => :features,
      :features_change => :change_features,
      :force_logout => :shutdown,
      :paths_whitelist => :change_whitelisted_paths,
      :ips_whitelist => :change_whitelisted_ips,
      :get_bundle => :upload_bundle,
      :performance_budget => :change_performance_budget,
    }.freeze

    attr_reader :uuid

    def initialize(json_desc)
      @name = json_desc['name'].to_sym
      @params = json_desc.fetch('params', [])
      @uuid = json_desc['uuid']
    end

    def process(runner, context_infos = {})
      failing = validate_command(runner)
      return failing if failing
      Sqreen.log.debug { format('processing command %s', @name) }
      begin
        output = runner.send(KNOWN_COMMANDS[@name], *@params, context_infos)
      rescue => e
        Sqreen::RemoteException.record(e)
        return { :status => false, :reason => "error: #{e.inspect}" }
      end
      format_output(output)
    end

    def self.process_list(runner, commands, context_infos = {})
      res_list = {}

      return res_list unless commands

      unless commands.is_a? Array
        Sqreen.log.debug { format('Wrong commands type %s', commands.class) }
        Sqreen.log.debug { commands.inspect }
        return res_list
      end
      commands.each do |cmd_json|
        Sqreen.log.debug { cmd_json }
        cmd = RemoteCommand.new(cmd_json)
        Sqreen.log.debug { cmd.inspect }
        uuid = cmd.uuid
        res_list[uuid] = cmd.process(runner, context_infos)
      end
      res_list
    end

    def to_h
      {
        :name => @name,
      }
    end

    protected

    def validate_command(runner)
      unless KNOWN_COMMANDS.include?(@name)
        msg = format("unknown command name '%s'", @name)
        Sqreen.log.debug { msg }
        return { :status => false, :reason => msg }
      end
      return nil if runner.respond_to?(KNOWN_COMMANDS[@name])
      msg = format("not implemented '%s'", @name)
      Sqreen.log.debug { msg }
      { :status => false, :reason => msg }
    end

    def format_output(output)
      case output
      when NilClass
        return { :status => false, :reason => 'nil returned' }
      when TrueClass
        return { :status => true }
      else
        return { :status => true, :output => output }
      end
    end
  end
end