require 'openssl'
require 'base64'

module Shared
  module HmacSignature

    def headers_sign headers, config, names = ['date']
      # Extract and check
      return unless config.is_a? Hash
      hmac_method = config[:method]
      hmac_user   = config[:user]
      hmac_secret = config[:secret]
      #log_debug "headers_sign config", config

      # Check params
      unless config[:method] && config[:user] && config[:secret]
        log_error "headers_sign: missing method/user/secret"
        return
      end

      # Check params
      unless config[:method] == 'hmac-kong'
        log_error "headers_sign: only [hmac-kong] method is supported"
        return
      end

      # OK, lets go
      hmac_sign_kong headers, config[:user], config[:secret], names
      # log_info "headers_sign: after signing", headers
    end

    def headers_md5 headers, payload
      headers['Content-MD5'] = Digest::MD5.hexdigest(payload.to_s)
    end

  private

    def hmac_sign_kong headers, client_id, client_secret, names
      # Update date
      headers['Date'] = Time.now.strftime('%a, %d %b %Y %H:%M:%S GMT')
      # headers['Content-MD5'] = Date.now.strftime('%a, %d %b %Y %H:%M:%S GMT')
      # log_debug "hmac_sign_kong: headers", headers

      # Filter headers we're going to hash
      myheaders = hmac_headers_filter headers, names

      # Signe string of headers
      signature = hmac_headers_hash myheaders, client_secret
      log_debug "hmac_sign_kong signed [#{signature}] from headers #{myheaders.keys.inspect}"

      # Add auth header
      headers['Authorization'] = hmac_build_header(client_id, myheaders, signature)
      #headers['test'] = "testing123"

      # That's OK
      return headers
    end

    def hmac_build_header client_id, myheaders, signature
      sprintf 'hmac username="%s", algorithm="hmac-sha1", headers="%s", signature="%s"',
        client_id,
        myheaders.keys.map(&:downcase).join(' '),
        signature
    end

    def hmac_headers_filter headers, selection
      out = {}

      # Build array of keys as strings, downcase
      selection_names = selection.map{|h| h.to_s.downcase}

      # For each header, stack it or not
      headers.each do |name, value|
        name_down = name.downcase
        next unless selection_names.include? name_down
        out[name_down] = value
      end

      # We're done
      return out
    end

    def hmac_headers_hash myheaders, client_secret
      # Build headers string
      data = myheaders.map do |name, value|
        sprintf("%s: %s", name, value)
      end.join("\n")

      # Hash this
      digest  = OpenSSL::Digest.new('sha1')
      Base64.encode64(OpenSSL::HMAC.digest(digest, client_secret, data)).strip
    end

    def hmac_sign_data client_secret, data
    end

  end
end