require 'active_support/core_ext/string'
module Remixr
  class Client
    include HTTParty
    base_uri 'api.remix.bestbuy.com/v1'
    format :json
    
    attr_reader :api_key, :store_filters, :product_filters
    
    # Get your api_key found here http://remix.bestbuy.com/apps/register
    def initialize(api_key=nil)
      @api_key = api_key
      @api_key ||= Remixr.api_key
      
      @api_path = ''
      @store_filters = {}
      @product_filters = {}
    end
    
    # Example filters:
    # Stores within 50 miles of ZIP 76227
    #   stores({:area => [76227,50]})
    #   GET /v1/stores(area(76227,50))
    #  
    # Stores west of the Pecos
    #   stores({:lng => {'$lt' => -104.304199}})
    #   GET /v1/stores(lng>-104.304199)
    #
    def stores(filters={})
      unless @api_path.include?('stores()')
        @api_path += '+' unless @api_path.blank?
        @api_path += "stores()"
      end
      @store_filters.merge!(filters)
      self
    end
    
    # Convenience method for finding a store by zip
    #   stores.in_zip(76227)
    #   GET /v1/stores(postalCode=76227)
    def in_zip(zip)
      self.stores({'postalCode' => zip})
    end
    
    # Convenience method for finding a store by region
    #   stores.in_region('TX')
    #   GET /v1/stores(region=TX)
    def in_region(region)
      self.stores({'region' => region})
    end
    
    # Example filters:
    # Products under 20 bucks
    #   products({:salePrice => {'$lt' => 20.00}})
    #   GET /v1/products(salePrice<20.00)
    def products(filters={})
      unless @api_path.include?('products()')
        @api_path += '+' unless @api_path.blank?
        @api_path += "products()"
      end
      @product_filters.merge!(filters)
      self
    end
    
    
    # Executes the search
    # Possible options:
    # :page - positive integer for page number
    # :show - comma delimited string or array of field names to show
    # :sort - hash or string of sort info {'fieldname' => 'asc|desc'}
    def fetch(options={})
      opts = {:apiKey => @api_key, :format => 'json'}
      opts.merge!(scrub_options(options))
      
      apply_store_filters
      apply_product_filters
      @api_path = URI.escape(@api_path)
      response = self.class.get("/" + @api_path, :query => opts)
      @api_path = ''
      Mash.new response
    end
    
    private
      def apply_store_filters
        if @store_filters.keys.blank? 
          @api_path.gsub!("stores()", "stores")
        else
          @api_path.gsub!("stores()", "stores(#{filter_params_string(@store_filters)})")
        end
      end
      
      def apply_product_filters
        if @product_filters.keys.blank? 
          @api_path.gsub!("products()", "products")
        else
          @api_path.gsub!("products()", "products(#{filter_params_string(@product_filters)})")
        end
      end
      
      def filter_params_string(filters)
        return "" unless filters.is_a?(Hash)
        
        filters = Mash.new filters.to_hash # calling Mash.new on a mash is bad mmmm k?
        params = []
        filters.each do |key, value|
          if value.is_a?(Hash)
            inner_hash = value
            case inner_hash.keys.first
            when "$gt"
              params << "#{key}>#{inner_hash.values.first}"
            when "$lt"
              params << "#{key}<#{inner_hash.values.first}"
            when "$gte"
              params << "#{key}>=#{inner_hash.values.first}"
            when "$lte"
              params << "#{key}<=#{inner_hash.values.first}"
            when "$ne"
              params << "#{key}!=#{inner_hash.values.first}"
            when "$in"
              params << "#{key} in(#{inner_hash.values.first.join(',')})"
            end
          elsif value.is_a?(Array)
            params << "#{key}(#{value.join(',')})"
          else
            params << "#{key}=#{value}"
          end
        end
        
        if params.blank?
          ""
        else
          "#{params.join('&')}"
        end
      end
      
      def scrub_options(options)
        options = Mash.new(options.to_hash)
        show = options.delete('show')
        unless show.blank?
          if show.is_a?(String)
            options['show'] = show
          elsif show.is_a?(Array)
            options['show'] = show.join(',')
          end
        end
        sort = options.delete('sort')
        unless sort.blank?
          if sort.is_a?(Hash)
            options['sort'] = "#{sort.keys.first}.#{sort.values.first}"
          else
            options['sort'] = sort
          end
        end
        options
      end
    
  end
end