require 'rack/request'
require 'rack/utils'
require 'rack/mock'

module JSONAPIonify::Api
  class Server::Request < Rack::Request
    def self.env_for(url, method, options = {})
      options[:method] = method
      new Rack::MockRequest.env_for(url, options)
    end

    def headers
      env_headers                 = env.select do |name, _|
        name.start_with?('HTTP_') && !%w{HTTP_VERSION}.include?(name)
      end.each_with_object({}) do |(name, value), hash|
        hash[name[5..-1].gsub('_', '-').downcase] = value
      end
      env_headers['content-type'] = content_type
      Rack::Utils::HeaderHash.new(env_headers)
    end

    def http_string
      # GET /path/file.html HTTP/1.0
      # From: someuser@jmarshall.com
      # User-Agent: HTTPTool/1.0
      # [blank line here]
      [].tap do |lines|
        lines << "#{request_method} #{fullpath} HTTP/1.1"
        headers.each do |k, v|
          lines << "#{k.split('-').map(&:capitalize).join('-')}: #{v}"
        end
        lines << ''
        lines << pretty_body if has_body?
      end.join("\n")
    end

    def pretty_body
      return '' unless has_body?
      value = body.read
      JSON.pretty_generate(Oj.load(value))
    rescue Oj::ParseError
      value
    ensure
      body.rewind
    end

    def content_type
      super.try(:split, ';').try(:first)
    end

    def accept
      accepts = (headers['accept'] || '*/*').split(',')
      accepts.to_a.sort_by! do |accept|
        _, *media_type_params = accept.split(';')
        rqf                   = media_type_params.find { |mtp| mtp.start_with? 'q=' }
        -(rqf ? rqf[2..-1].to_f : 1.0)
      end.map do |accept|
        mime, *media_type_params = accept.split(';')
        media_type_params.reject! { |mtp| mtp.start_with? 'q=' }
        [mime, *media_type_params].join(';')
      end
    end

    def authorizations
      parts = headers['authorization'].to_s.split(' ')
      parts.length == 2 ? Rack::Utils::HeaderHash.new([parts].to_h) : {}
    end

    def has_body?
      body.read(1).present?
    ensure
      body.rewind
    end

    def root_url
      URI.parse(url).tap do |uri|
        uri.query = nil
        uri.path.chomp! path_info
      end.to_s
    end
  end
end