require 'sendgrid_subusers/api_request'
require 'net/http'
require 'uri'
require 'json'

module SendgridSubusers
  class ApiRequest

    def make_request(action, arg_hash)
      uri = action_uri(action)

      response = http(uri).request(request_params(uri, arg_hash, action))

      response_results = parse_results(response.body)

      if response.code.to_i > 401
        raise(SendgridSubuserError::SendgridServerError, "The sengrid server returned an error. #{response.inspect}")
      elsif has_error?(response_results) and
          response_results['error'].respond_to?(:has_key?) and
          response_results['error'].has_key?('code') and
          response_results['error']['code'].to_i == 401
        raise SendgridSubuserError::AuthenticationFailed
      elsif has_error?(response_results)
        raise(SendgridSubuserError::APIError, response_results['error'].to_s)
      end

      response_results
    end

    def action_uri(action)
      URI.parse(api_uri(action) + api_actions[action])
    end

    def has_error?(response)
      response.kind_of?(Hash) && response.has_key?('error')
    end

    def request_params(uri, arg_hash, action)
      request_params = Net::HTTP::Get.new(uri.path)
      request_params.set_form_data(build_args_uri(action, arg_hash))
      request = Net::HTTP::Get.new( uri.path + '?' + request_params.body )
    end

    def http(uri)
      http = Net::HTTP.new(uri.host, uri.port)
      http.use_ssl = true
      http.verify_mode = OpenSSL::SSL::VERIFY_NONE
      return http
    end

    def api_credentials
      {'api_user' => @api_user, 'api_key' => @api_key}
    end

    def api_uri(action)
      if action == :bounces
        "https://sendgrid.com/api/"
      else
        "https://sendgrid.com/apiv2/"
      end
    end

    def api_actions
      {
        :add => "customer.add.json",
        :delete => "customer.delete.json",
        :update_password => "customer.password.json",
        :update_profile => "customer.profile.json",
        :update_username => "customer.profile.json",
        :update_email => "customer.profile.json",
        :retrieve => "customer.profile.json",
        :enable => "customer.enable.json",
        :disable => "customer.disable.json",
        :website_access_disable => "customer.website_disable.json",
        :website_access_enable => "customer.website_enable.json",
        :ip_use_all => "customer.sendip.json",
        :ip_use_none => "customer.sendip.json",
        :ip_specify => "customer.sendip.json",
        :user_ips => "customer.sendip.json",
        :whitelabel_specify => "customer.whitelabel.json",
        :whitelabel_list => "customer.whitelabel.json",
        :enable_app => "customer.apps.json",
        :disable_app => "customer.apps.json",
        :setup_dkim_app => "customer.apps.json",
        :setup_notification_app => "customer.apps.json",
        :user_stats => "customer.stats.json",
        :bounces => "user.bounces.json",
        :invalid_emails => "customer.invalidemails.json"
      }
    end

    def parse_results(json_string)
      begin
        JSON.parse(json_string)
      rescue JSON::ParserError => e
        return nil
      end
    end

    def validate_update_profile_args(arg_hash)
      valid_args = ['first_name', 'last_name', 'address', 'city', 'state', 'country', 'zip', 'phone', 'website', 'company']
      arg_hash.each_key { |key| if not valid_args.include?(key.to_s); raise SendgridSubuserError::ProfileUpdateArgsError, "The profile option '#{key.to_s}' is invalid. Valid arguments are #{valid_args.to_s.gsub(/\[|\]/,"")}." end }
      return true
    end

    def validate_retrieve_args(arg_hash)
      valid_args = ['username', 'email', 'active', 'first_name', 'last_name', 'address', 'city', 'state', 'country', 'zip', 'phone', 'website', 'company']
      arg_hash.each_key { |key| if not valid_args.include?(key.to_s); raise SendgridSubuserError::RetrieveArgsError, "The retrieve option '#{key.to_s}' is invalid. Valid arguments are #{valid_args.to_s.gsub(/\[|\]/,"")}." end }
      return true
    end

    def validate_add_args(arg_hash)
      valid_args = ['username', 'password', 'confirm_password', 'first_name', 'last_name', 'address', 'city', 'state', 'zip', 'email', 'country', 'phone', 'website', 'company']
      arg_hash.each_key { |key| if not valid_args.include?(key.to_s); raise SendgridSubuserError::RetrieveArgsError, "The retrieve option '#{key.to_s}' is invalid. Valid arguments are #{valid_args.to_s.gsub(/\[|\]/,"")}." end }
      return true
    end

    def build_args_uri(action, arg_hash)
      case action
      when :add, :delete, :update_password
        return api_credentials.merge(arg_hash)
      when :disable, :enable, :website_access_enable, :website_access_disable, :retrieve, :bounces, :invalid_emails
        return api_credentials.merge(arg_hash).merge({'task' => 'get'})
      when :ip_use_all
        return api_credentials.merge(arg_hash).merge({'set' => 'all', 'task' => 'append'})
      when :ip_use_none
        return api_credentials.merge(arg_hash).merge({'set' => 'none', 'task' => 'append'})
      when :ip_specify
        return api_credentials.merge(arg_hash).merge({'set' => 'specify', 'task' => 'append'})
      when :update_profile
        return api_credentials.merge(arg_hash).merge({'task' => 'set'})
      when :update_username
        return api_credentials.merge(arg_hash).merge({'task' => 'setUsername'})
      when :update_email
        return api_credentials.merge(arg_hash).merge({'task' => 'setEmail'})
      when :whitelabel_specify
        return api_credentials.merge(arg_hash).merge({'task' => 'append'})
      when :whitelabel_list, :user_ips
        return api_credentials.merge(arg_hash).merge({'task' => 'list'})
      when :enable_app
        return api_credentials.merge(arg_hash).merge({'task' => 'activate'})
      when :disable_app
        return api_credentials.merge(arg_hash).merge({'task' => 'deactivate'})
      when :setup_dkim_app
        return api_credentials.merge(arg_hash).merge({'task' => 'setup', 'name' => 'dkim'})
      when :setup_notification_app
        return api_credentials.merge(arg_hash).merge({'task' => 'setup', 'name' => 'eventnotify'})
      when :user_stats
        return api_credentials.merge(arg_hash).merge({'aggregate' => '1'})
      end
    end
  end
end
