require_relative 'parser'
require_relative 'argbucket'
require_relative 'log'

class Call < ArgBucket

  def initialize(value, args)
    @arg_bucket = args
    @plugin = value['plugin']
    begin
      require_relative "#{PLUGINS}#{@plugin}" unless @plugin.nil?
    rescue LoadError
      puts 'Plugin failed to load. Custom functions will not be run.'
      @plugin = nil
    end
    @function = value['function']
    credential_sub get('username'), get('password')
    @mode = value['mode']
    check_constraints
    @output = value['output']
    @logging = value['logging']
    @stdout = value['stdout']
    set_call_args value['input'] unless value['input'].nil?
  end

  def check_constraints
    if @mode.nil? then fail '"mode" not set. syntax= mode: get/post/put/delete.' end
    if @function.nil? then fail '"function" not set. syntax= function: api-url.' end
  end

  def credential_sub(username, password)
    @function.gsub!('username', username) unless username.nil?
    @function.gsub!('password', password) unless password.nil?
  end

  def set_call_args(input)
    @input = {}
    input.each do |arg|
      value = get arg
      if arg.nil? then puts "WARNING: required argument '#{arg}' not set." end
      @input[arg] = value
    end
  end

  def run
    case @mode
      when 'get', 'put', 'post', 'delete'
        rest_request
      when 'curl_get'
        command = "curl -v --url #{@function}"
        curl_request command
      when 'curl_post'
        command = "curl -v -X POST --url #{@function}"
        curl_request command
        @response = `#{command}`
      when 'plugin'
        Plugin.new(@response).run
      else
        puts "#{@mode} not supported."
    end
    if !@response.nil?
      @code = @response.code unless @response.is_a? String
      xml_to_json
      if @logging == true then log_response end
      parse_response unless @output.nil?
    end
    return @arg_bucket
  end

  def curl_request(command)
    @input.each_pair do |key, value|
      command << " -F #{key}='#{value}'"
    end
    @response = `#{command}`
  end

  def rest_request
    @response = RestClient::Request.execute(method: @mode, url: @function, headers: {params: @input}) { |response, request, result, &block|
      if [301, 302, 307].include? response.code
        response.follow_redirection(request, result, &block)
      else
        response.return!(request, result, &block)
      end
    }
    rescue RestClient::Unauthorized
      puts 'Username or password was invalid.'
    rescue RestClient::Exception
      puts 'Internal Error or requested resource was not found.'
  end

  def log_response
    logger = ResponseLogger.new
    logger.object_log @code, @response
  end

  def xml_to_json
    @response = Hash.from_xml(@response).to_json unless !@response.include?('<?xml')
  end

  def parse_response
    @response = JSON.parse @response
    if @stdout=='true' then puts JSON.pretty_generate @response end
    @output.each do |var|
      if var.is_a? String
        parser = Parser.new(var, nil, @response)
        parser.parse
        value = parser.output
        set "#{var}", value
      elsif var.is_a? Hash
        parser = Parser.new(var.first[0], get(var.first[1]), @response)
        parser.parse
        value = parser.output
        set var.first[0], value
      end
    end
  end
end