module RedisAnalytics
  module VisitsHelper
    
    FORMAT_SPECIFIER = [['%Y', 365], ['%m', 30], ['%d', 24], ['%H', 60], ['%M', 60]]

    GRANULARITY = ['yearly', 'monthly', 'dayly', 'hourly', 'minutely']

    private
    def method_missing(meth, *args, &block)
      if meth.to_s =~ /^(minute|hour|dai|day|month|year)ly_([a-z_0-9]+)$/
        granularity = ($1 == 'dai' ? 'day' : $1) + 'ly'
        metric_name = $2
        data(granularity, metric_name, *args)
      else
        super
      end
    end

    def metric_type(metric_name)
      RedisAnalytics.redis_connection.hget("#{RedisAnalytics.redis_namespace}:#METRICS", metric_name)
    end

    def data(granularity, metric_name, from_date, options = {})
      aggregate = options[:aggregate] || false
      x = granularity[0..-3]

      to_date = (options[:to_date] || Time.now).send("end_of_#{x}")
      i = from_date.send("beginning_of_#{x}")

      union = []
      time = []
      begin
        slice_key = i.strftime(FORMAT_SPECIFIER[0..GRANULARITY.index(granularity)].map{|x| x[0]}.join('_'))
        union << "#{RedisAnalytics.redis_namespace}:#{metric_name}:#{slice_key}"
        time << slice_key.split('_')
        i += 1.send(x)
      end while i <= to_date
      seq = get_next_seq
      if metric_type(metric_name) == 'String'
        if aggregate
          union_key = "#{RedisAnalytics.redis_namespace}:#{seq}"
          RedisAnalytics.redis_connection.zunionstore(union_key, union)
          RedisAnalytics.redis_connection.expire(union_key, 100)
          return Hash[RedisAnalytics.redis_connection.zrange(union_key, 0, -1, :with_scores => true)]
        else
          return time.zip(union.map{|x| Hash[RedisAnalytics.redis_connection.zrange(x, 0, -1, :with_scores => true)]})
        end
      elsif metric_type(metric_name) == 'Integer'
        if aggregate
          return RedisAnalytics.redis_connection.mget(*union).map(&:to_i).inject(:+)
        else
          return time.zip(RedisAnalytics.redis_connection.mget(*union).map(&:to_i))
        end
      else
        if Metrics.public_instance_methods.any?{|m| m.to_s =~ /^#{metric_name}_ratio_per_(hit|visit)$/}
          aggregate ? {} : time.zip([{}] * time.length)
        elsif Metrics.public_instance_methods.any?{|m| m.to_s =~ /^#{metric_name}_count_per_(hit|visit)$/}
          aggregate ? 0 : time.zip([0] * time.length)
        else
          aggregate ? 0 : time.zip([0] * time.length)
        end
      end
    end

    def get_next_seq
      seq = RedisAnalytics.redis_connection.incr("#{RedisAnalytics.redis_namespace}:#SEQUENCER")
    end

    def time_range
      (request.cookies["_rarng"] || RedisAnalytics.default_range).to_sym
    end
  end
end

