module Bearcat
  class ApiArray
    include Enumerable

    attr_reader :status, :headers, :members

    def self.process_response(response, api_client)
      if response.is_a?(Array)
        ApiArray.new(response, api_client)
      elsif key = array_key(response)
        ApiArray.new(response, api_client, key)
      else
        response.body
      end
    end

    def initialize(response, api_client, array_key = nil)
      @api_client = api_client
      @array_key = array_key
      case response.status
        when 200..206
          @members = process_body(response)
          @status = response.status
          @headers = response.headers
          @method = response.env[:method]
          init_pages(@headers['Link'])
      end
    end

    def [](i)
      @members[i]
    end

    def last
      @members.last
    end

    def each(&block)
      @members.each { |member| block.call(member) }
    end

    def pages?
      @link_hash['next'] || @link_hash['prev']
    end

    def next_page
      load_page('next')

    end

    def prev_page
      load_page('prev')
    end

    def first_page
      load_page('first')
    end

    def last_page
      load_page('last')
    end

    def all_pages!(page_count = 50)
      if pages?
        @page_count = page_count
        response = get_page(@link_hash['first'])
        @headers = response.headers
        @status = response.status
        @method = response.env[:method]
        init_pages(@headers[:link])
        @members = process_body(response)
        while @link_hash['next']
          response = get_page(@link_hash['next'])
          @headers = response.headers
          @status = response.status
          @method = response.env[:method]
          @members.concat(process_body(response))
          init_pages(@headers[:link])
        end
        @link_hash = {}
      end
      self
    end

    private

    def init_pages(link_header)
      @link_hash = {}
      if @headers.has_key? 'Link'
        links = link_header.split(/,\s?/)

        links.each do |link|
          link_parts = link.split(/;\s?/)
          url = link_parts.shift.strip.gsub(/^<|>$/, '')
          rel = link_parts.find { |part| part.gsub('"', '').split('=').first == 'rel' }
          @link_hash[rel.gsub('"', '').split('=').last] = url
        end
      end
    end

    def get_page(url, params = {})
      params['per_page'] = @page_count unless params.has_key? 'per_page' || !@page_count
      query = URI.parse(url).query
      p = CGI.parse(query).merge(params)
      u = url.gsub("?#{query}", '')
      p.each { |k, v| p[k] = v.first if v.is_a?(Array) }
      @api_client.connection.send(:get) do |r|
        r.url(u, p)
      end
    end

    def load_page(rel)
      if @link_hash.has_key? rel
        response = get_page(@link_hash[rel])
        ApiArray.process_response(response, @api_client)
      end
    end

    #TODO: This is a quick fix for JSONAPI responses if we need to do this for anything else we need to do this a better way
    def self.array_key(response)
      if response.env[:method] == :get && response.env[:url].path.match(/.*\/(courses||groups)\/\d+\/conferences/)
        'conferences'
      end
    end

    def process_body(response)
      if response.body.is_a?(Array)
        response.body
      elsif response.body.is_a?(Hash) && @array_key
        response.body[@array_key]
      end
    end

  end
end