require 'uri'
require 'net/https'

module RubyPushNotifications
  module MPNS
    # Encapsulates a connection to the MPNS service
    # Responsible for final connection with the service.
    #
    class MPNSConnection

      # @private Content-Type HTTP Header string
      CONTENT_TYPE_HEADER  = 'Content-Type'

      # @private text/xml content type
      X_NOTIFICATION_CLASS = 'X-NotificationClass'

      # @private Windows Phone Target
      X_WINDOWSPHONE_TARGET = 'X-WindowsPhone-Target'

      # @private text/xml content type
      XML_CONTENT_TYPE     = 'text/xml'

      # @private Enumators for notification types
      BASEBATCH = { tile: 1, toast: 2, raw: 3 }

      # @private Enumators for delay
      BATCHADDS = { delay450: 10, delay900: 20 }

      # @private Windows Phone Target Types
      WP_TARGETS = { toast: 'toast', tile: 'token' }

      # Issues a POST request to the MPNS send endpoint to
      # submit the given notifications.
      #
      # @param n [MPNSNotification]. The notification object to POST
      # @param cert [String] optional. Contents of the PEM encoded certificate.
      # @param options [Hash] optional. Options for GCMPusher. Currently supports:
      #   * open_timeout [Integer]: Number of seconds to wait for the connection to open. Defaults to 30.
      #   * read_timeout [Integer]: Number of seconds to wait for one block to be read. Defaults to 30.
      # @return [Array]. The response of post
      # (http://msdn.microsoft.com/pt-br/library/windows/apps/ff941099)
      def self.post(n, cert = nil, options = {})
        headers = build_headers(n.data[:type], n.data[:delay])
        body = n.as_mpns_xml
        responses = []
        n.each_device do |url|
          http = Net::HTTP.new url.host, url.port
          http.open_timeout = options.fetch(:open_timeout, 30)
          http.read_timeout = options.fetch(:read_timeout, 30)
          if cert && url.scheme == 'https'
            http.use_ssl = true
            http.verify_mode = OpenSSL::SSL::VERIFY_PEER
            http.ca_file = cert
          end
          response = http.post url.path, body, headers
          responses << { device_url: url.to_s, headers: extract_headers(response), code: response.code.to_i }
        end
        MPNSResponse.new responses
      end

      # Build Header based on type and delay
      #
      # @param type [Symbol]. The type of notification
      # @param delay [Symbol]. The delay to be used
      # @return [Hash]. Correct delay based on notification type
      def self.build_headers(type, delay)
        headers = {
          CONTENT_TYPE_HEADER => XML_CONTENT_TYPE,
          X_NOTIFICATION_CLASS => "#{(BASEBATCH[type] + (BATCHADDS[delay] || 0))}"
        }
        headers[X_WINDOWSPHONE_TARGET] = WP_TARGETS[type] unless type == :raw
        headers
      end

      # Extract headers from response
      # @param response [Net::HTTPResponse]. HTTP response for request
      #
      # @return [Hash]. Hash with headers with case-insensitive keys and string values
      def self.extract_headers(response)
        headers = {}
        response.each_header { |k, v| headers[k] = v }
        headers
      end

    end
  end
end
