require 'json'

module HiGCM

  class Handler

    attr_accessor :retry_conditions, :unregister_conditions

    def initialize
      @retry_ids   = {}
      @unreg_ids   = {}
      @fail_ids    = {}
      @success_ids = {}
      @renew_ids   = {}

      @retry_conditions      = [ 'InternalServerError', 'Timout', 'Unavailable' ]
      @unregister_conditions = [ 'NotRegistered', 'InvalidRegistration' ]
    end

    def handle(registration_ids, opts, response)
      begin
        @code = response.code.to_i
        @body = JSON.parse(response.body)
        #Honor Retry-After
        @retry_after = (response.headers['Retry-After'].nil? ? 0 : response.headers['Retry-After'])
      rescue Exception => e
        @code = 99
        @error_message = "unexpected error, response: #{response.body}, exception: #{e.inspect}"
      end

      case @code
      when 200
        #200 Message was processed successfully. The response body will contain more details about the message status, but its format will depend whether the request was JSON or plain text. See Interpreting a success response for more details.
        # handle success case, http://developer.android.com/guide/google/gcm/gcm.html#success
        @body['results'].each_with_index do | rs, index |
          reg_id = registration_ids[index]
          #handle fail
          if rs.key?('error')
            @error_message = rs['error']
            # figure out retry stuff
            if @retry_conditions.include?(rs['error'])
              @retry_ids[reg_id] = @retry_after
              @error_message << ", retry after #{@retry_after}"
            end
            # figure out unregister stuff
            if @unregister_conditions.include?(rs['error'])
              @unreg_ids[reg_id] = rs['error']
            end
            @fail_ids[reg_id] = @error_message
          #handle success
          elsif rs.key?('message_id')
            @success_ids[reg_id] = rs['message_id']
            if rs.key?('registration_id')
              @renew_ids[reg_id] = rs['registration_id']
            end
          else
            #should not jump here
          end
        end
        @do_success.call(@success_ids, response)        if @success_ids.count > 0 && @do_success
        @do_fail.call(@fail_ids, response)              if @fail_ids.count > 0 && @do_fail
        @do_renew_token.call(@renew_ids, response)      if @renew_ids.count > 0 && @do_renew_token
        @do_retry.call(@retry_ids, opts, response)      if @retry_ids.count > 0 && @do_retry
        @do_unregister_token.call(@unreg_ids, response) if @unreg_ids.count > 0 && @do_unregister_token

      #TODO need to check what kinf of response will return
      when 400, 401
        #400 Only applies for JSON requests. Indicates that the request could not be parsed as JSON, or it contained invalid fields (for instance, passing a string where a number was expected). The exact failure reason is described in the response and the problem should be addressed before the request can be retried.
        #401 There was an error authenticating the sender account.
        registration_ids.each do | reg_id |
          if 400 == @code
            error_message = 'request could not be parsed as JSON'
          else
            error_message = 'There was an error authenticating the sender account'
          end
          @fail_ids[reg_id] = error_message
        end
        @do_fail.call(@fail_ids, response) if @do_fail &&  @fail_ids.count > 0
      #TODO need to check what kinf of response will return
      when 500, 503
        #500 There was an internal error in the GCM server while trying to process the request. trouble shooting http://developer.android.com/guide/google/gcm/gcm.html#internal_error
        #503 Indicates that the server is temporarily unavailable (i.e., because of timeouts, etc ). Sender must retry later, honoring any Retry-After header included in the response. Application servers must implement exponential back-off. The GCM server took too long to process the request. Troubleshoot
        registration_ids.each do | reg_id |
          @retry_ids[reg_id] = @retry_after
        end
        @do_retry.call(@retry_ids, opts) if @do_retry && @retry_ids.count > 0
      else
        registration_ids.each do | reg_id |
          @fail_ids[reg_id] = @error_message
        end
        @do_fail.call(@fail_ids, response) if @do_fail && @fail_ids.count > 0
      end
    end

    # success_ids
    def do_success(&block)
      @do_success = block
    end

    # retry_ids, opts, retry_after
    def do_retry(&block)
      @do_retry = block
    end

    def do_fail(&block)
      @do_fail = block
    end

    # renew_ids(hash)
    def do_renew_token(&block)
      @do_renew_token = block
    end

    def do_unregister_token(&block)
      @do_unregister_token = block
    end

  end
end
