class AggregateDataQuery
  attr_accessor :router_inventories, :lns, :query_for, :criteria

  #Initilize Query object with query params.
  # query - [<start-date>, <end-date>] OR Any one of ["1d", "3d", "1w", "1m", "3m", "6m"]
  def initialize query, ris=[], ln_ids=[]
    #Set all query params
    @router_inventories = ris
    @lns = ln_ids
    @query_for = query
    @criteria = find_criteria
  end

  def aps_throughput
    ApAggregateData.collection.aggregate({"$match" => {"mac"=>{"$in"=> router_inventories},
                                                       "ln" => {"$in" => lns},
                                                       "d" => {"$gte"=> criteria[:st], "$lte"=> criteria[:et] }}},
                                        {"$project" => {"mac" => 1, "hr.h" => 1, "hr.tx_r" => 1, "hr.rx_r" => 1, "d" => 1, "hr.tx_u" => 1, "hr.rx_u" => 1}},
                                        {"$unwind" => "$hr"},
                                        {"$group" => {"_id" => criteria[:group_criteria],
                                                      "tx" => {"$sum" => "$hr.tx_r"},
                                                      "rx" => {"$sum" => "$hr.rx_r"},
                                                      "tx_u" => {"$sum" => "$hr.tx_u"},
                                                      "rx_u" => {"$sum" => "$hr.rx_u"}}},
                                        {"$sort" => {"_id.d" => 1}})
  end

  def clients_throughput
    ClientAggregateData.collection.aggregate({"$match" => {"ap"=>{"$in"=> router_inventories},
                                                           "ln" => {"$in" => lns},
                                                           "d" => {"$gte"=> criteria[:st], "$lte"=> criteria[:et] }}},
                                            {"$project" => {"ap" => 1, "mac" => 1, "hr.h" => 1, "hr.tx_r" => 1, "hr.rx_r" => 1, "d" => 1}},
                                            {"$unwind" => "$hr"},
                                            {"$group" => {"_id" => criteria[:group_criteria],
                                                          "tx" => {"$sum" => "$hr.tx_r"},
                                                          "rx" => {"$sum" => "$hr.rx_r"}}},
                                            {"$sort" => {"_id.d" => 1}})
  end

  def client_throughput client_mac
    ClientAggregateData.collection.aggregate({"$match" => {"mac" => {"$eq" => client_mac},
                                                           "ap"=>{"$in"=> router_inventories},
                                                           "ln" => {"$in" => lns},
                                                           "d" => {"$gte"=> criteria[:st], "$lte"=> criteria[:et] }}},
                                            {"$project" => {"ap" => 1, "mac" => 1, "hr.h" => 1, "hr.tx_r" => 1, "hr.rx_r" => 1, "d" => 1}},
                                            {"$unwind" => "$hr"},
                                            {"$group" => {"_id" => criteria[:group_criteria],
                                                          "tx" => {"$avg" => "$hr.tx_r"},
                                                          "rx" => {"$avg" => "$hr.rx_r"}}},
                                            {"$sort" => {"_id.d" => 1}})
  end

  def connected_clients_count
    q_params = self.criteria.deep_dup
    q_params[:group_criteria].merge!({"ap" => "$ap"})
    ClientAggregateData.collection.aggregate({"$match" => {"ap" => {"$in"=> router_inventories},
                                                           "ln" => {"$in" => lns},
                                                           "d" => {"$gte"=> q_params[:st], "$lte"=> q_params[:et] }}},
                                            {"$project" => {"ap" => 1, "mac" => 1, "hr.h" => 1, "d" => 1}},
                                            {"$unwind" => "$hr"},
                                            {"$group" => {"_id" => q_params[:group_criteria],
                                                          "clis" => {"$addToSet" => "$mac"}}},
                                            {"$project" => {"count" => {"$size" => "$clis"}}},
                                            {"$sort" => {"_id.d" => 1, "count" => -1}},
                                            {"$group" => {"_id" => "$_id.d",
                                                          "aps" => {"$push" => "$_id.ap"},
                                                          "cli_count" => {"$push" => "$count"}}},
                                            {"$sort" => {"_id" => 1}})
  end

  def clients_total_usage skip, limit
    ClientAggregateData.collection.aggregate({"$match" => {"ap"=>{"$in"=> router_inventories},
                                                           "ln" => {"$in" => lns},
                                                           "d" => {"$gte"=> criteria[:st], "$lte"=> criteria[:et] }}},
                                             {"$project" => {"mac" => 1,"ap" => 1, "ip" =>1, "l_time" =>1 ,"hostname" => 1, "hr.h" => 1, "hr.tx_u" => 1, "hr.rx_u" => 1}},
                                             {"$unwind" => "$hr"},
                                             {"$group" => {"_id" => "$mac",
                                                           "tx" => {"$sum" => "$hr.tx_u"},
                                                           "rx" => {"$sum" => "$hr.rx_u"},
                                                           "ip" => { "$last" => "$ip"},
                                                           "ap" => { "$last" => "$ap"},
                                                           "hostname" => { "$last" => "$hostname"},
                                                           "l_time" => { "$last" => "$l_time"}
                                                         }},
                                             {"$project" => {"tx" => 1,
                                                             "rx" => 1,
                                                             "ip" => 1,
                                                             "l_time" => 1,
                                                             "hostname" => 1,
                                                             "ap" => 1,
                                                             "total" => {"$add" => ["$tx", "$rx"]}}},
                                             {"$sort" => {"total" => -1}},
                                             {"$skip" => skip},
                                             {"$limit" => limit})
  end

  def aps_total_usage skip, limit
    ApAggregateData.collection.aggregate({"$match" => {"mac"=>{"$in"=> router_inventories},
                                                       "ln" => {"$in" => lns},
                                                       "d" => {"$gte"=> criteria[:st], "$lte"=> criteria[:et] }}},
                                         {"$project" => {"mac" => 1, "hr.h" => 1, "hr.tx_u" => 1, "hr.rx_u" => 1}},
                                         {"$unwind" => "$hr"},
                                         {"$group" => {"_id" => "$mac",
                                                       "tx" => {"$sum" => "$hr.tx_u"},
                                                       "rx" => {"$sum" => "$hr.rx_u"}}},
                                         {"$project" => {"tx" => 1,
                                                         "rx" => 1,
                                                         "total" => {"$add" => ["$tx", "$rx"]}}},
                                         {"$sort" => {"total" => -1}},
                                         {"$skip" => skip},
                                         {"$limit" => limit})
  end

  def ssids_usage ssids=[]
    SsidAggregateData.collection.aggregate({"$match" => {"ap" => {"$in"=> router_inventories},
                                                         "ssid" => {"$in" => ssids},
                                                         "ln" => {"$in" => lns},
                                                         "d" => {"$gte"=> criteria[:st], "$lte"=> criteria[:et] }}},
                                           {"$project" => {"ap" => 1, "ssid" => 1, "name" => 1, "hour.h" => 1, "hour.tx_u" => 1, "hour.rx_u" => 1}},
                                           {"$unwind" => "$hour"},
                                           {"$group" => {"_id" => "$ssid",
                                                         "tx" => {"$sum" => "$hour.tx_u"},
                                                         "rx" => {"$sum" => "$hour.rx_u"},
                                                         "name" => {"$first" => "$name"}}},
                                           {"$project" => {"name" => 1,
                                                           "tx" => 1,
                                                           "rx" => 1,
                                                           "total" => {"$add" => ["$tx", "$rx"]}}},
                                           {"$sort" => {"total" => -1}})
  end

  def clients_count_per_ssids
    ClientAggregateData.collection.aggregate({"$match" => {"ap"=>{"$in"=> router_inventories},
                                                           "ln" => {"$in" => lns},
                                                           "d" => {"$gte"=> criteria[:st], "$lte"=> criteria[:et] }}},
                                             {"$project" => {"ap" => 1, "mac" => 1, "ssid" => 1}},
                                             {"$unwind" => "$ssid"},
                                             {"$group" => {"_id" => "$ssid.id",
                                                           "clis" => {"$addToSet" => "$mac"}}},
                                             {"$project" => {"count" => {"$size" => "$clis"}}},
                                             {"$sort" => {"count" => -1}})
  end

  def clients_count reports=false
    criteria[:group_criteria].delete "h" if reports
    ClientAggregateData.collection.aggregate({"$match" => {"ap"=>{"$in"=> router_inventories},
                                                           "ln" => {"$in" => lns},
                                                           "d" => {"$gte"=> criteria[:st], "$lte"=> criteria[:et] }}},
                                             {"$project" => {"ap" => 1, "mac" => 1, "hr.h" => 1, "d" => 1}},
                                             {"$unwind" => "$hr"},
                                             {"$group" => {"_id" => criteria[:group_criteria],
                                                           "clis" => {"$addToSet" => "$mac"}}},
                                             {"$project" => {"count" => {"$size" => "$clis"}}},
                                             {"$sort" => {"_id.d" => 1}})
  end

  def clients_devices_count
    ClientAggregateData.collection.aggregate({"$match" => {"ap"=>{"$in"=> router_inventories},
                                                           "ln" => {"$in" => lns},
                                                           "d" => {"$gte"=> criteria[:st], "$lte"=> criteria[:et] }}},
                                             {"$project" => {"mac" => 1, "device" => { "$ifNull" => [ "$device", 0 ] }}},
                                             {"$group" => {"_id" => "$mac",
                                                           "device" => {"$last" => "$device"}}},
                                             {"$group" => {"_id" => "$device",
                                                           "count" => {"$sum" => 1}}},
                                             {"$sort" => {"count" => -1}})
  end

  def distinct_clients_count
    #ClientAggregateData.where("ln" => {"$in" => lns}, "d" => {"$gte"=> criteria[:st], "$lte"=> criteria[:et] }).distinct("mac").count
    result = ClientAggregateData.collection.aggregate({"$match" => {"ln" => {"$in" => lns}, "d" => {"$gte" => criteria[:st], "$lte" => criteria[:et]}}}, {"$project" => {"mac" => 1}}, {"$group" => {"_id" => nil, "macs" => {"$addToSet" => "$mac"}}}, {"$project" => {"count" => {"$size" => "$macs"}}})
    return (result.first.blank? ? 0 : (result.first['count'] || 0))
  end

  def distinct_clients_count_for_ap mac
    ClientAggregateData.where("ap" => mac, "ln" => {"$in" => lns}, "d" => {"$gte"=> criteria[:st], "$lte"=> criteria[:et] }).distinct("mac").count
  end

  def up_aps
    ApAggregateData.where("ln" => {"$in" => lns}, "d" => {"$gte"=> criteria[:st], "$lte"=> criteria[:et] }).distinct("mac")
  end

  def get_aps_down_time aps,ln
    tz = ln.timezone || LocationNetwork::DEFAULT_TIMEZONE
    criteria[:st] = ActiveSupport::TimeZone.new(tz).parse(criteria[:st].strftime("%Y-%m-%d %H:%M")).utc
    if criteria[:et].to_date == Time.zone.now.in_time_zone(tz).to_date
      criteria[:et] = Time.zone.now
    else
      criteria[:et] = ActiveSupport::TimeZone.new(tz).parse(criteria[:et].strftime("%Y-%m-%d %H:%M")).end_of_day.utc
    end

    aps_down_time = ApDowntime.collection.aggregate({"$match" => {"mac" => {"$in" => aps},
                                                                "st" => {"$lt" => criteria[:et]},
                                                                "et" => {"$gt" => criteria[:st]}}},
                                    {"$project" => {"mac" => 1, "st" => 1, "et" => 1,
                                                    "est" => {"$cond" => [{"$gte" => ["$st", criteria[:st]]},
                                                                          "$st", criteria[:st]]},
                                                    "eet" => {"$cond" => [{"$lte" => ["$et", criteria[:et]]},
                                                                          "$et", criteria[:et]]},
                                                    "down_secs" => { "$divide" => [{"$cond" => [{"$gte" => ["$st", criteria[:st]]},
                                                                                  {"$cond" => [{"$lte" => ["$et", criteria[:et]]},
                                                                                               {"$subtract" => ["$et", "$st"]},
                                                                                               {"$subtract" => [criteria[:et], "$st"]}]},
                                                                                  {"$cond" => [{"$lt" => ["$et", criteria[:et]]},
                                                                                               {"$subtract" => ["$et", criteria[:st]]},
                                                                                               {"$subtract" => [criteria[:et], criteria[:st]]}]}]}, 1000]}}})
    aps_down_time = aps_down_time.group_by {|ap| ap['mac']}
    routers = RouterInventory.includes(:location_network).find_all_by_mac_id(aps)
    routers.map {|r| {'mac' => r.mac_id, 'name' => r.name, 'tags' => r.tag_list, 'down_time' => r.total_down_time(criteria, aps_down_time[r.mac_id] || []),'timezone' => r.location_network.timezone}}
  end

  def all_aps_uplinks_usage
    UplinkAggregateData.collection.aggregate({"$match" => {"mac"=>{"$in"=> router_inventories},
                                                       "ln" => {"$in" => lns},
                                                       "d" => {"$gte"=> criteria[:st], "$lte"=> criteria[:et] }}},
                                         {"$project" => {"mac" => 1, "hr.h" => 1, "hr.tx_u" => 1, "hr.rx_u" => 1,"utype" => 1,"uid" => 1}},
                                         {"$unwind" => "$hr"},
                                         {"$group" => {"_id" => {"mac" => "$mac","utype" => "$utype"},
                                                       "tx" => {"$sum" => "$hr.tx_u"},
                                                       "rx" => {"$sum" => "$hr.rx_u"}}},
                                         {"$project" => {"tx" => 1,
                                                         "rx" => 1,
                                                         "total" => {"$add" => ["$tx", "$rx"]}}})
  end

  def uplinks_total_usage skip,limit
    UplinkAggregateData.collection.aggregate({"$match" => {"mac"=>{"$in"=> router_inventories},
                                                       "ln" => {"$in" => lns},
                                                       "d" => {"$gte"=> criteria[:st], "$lte"=> criteria[:et] }}},
                                         {"$project" => {"mac" => 1, "hr.h" => 1, "hr.tx_u" => 1, "hr.rx_u" => 1,"utype" => 1,"uid" => 1}},
                                         {"$unwind" => "$hr"},
                                         {"$group" => {"_id" => {"mac" => "$mac","utype" => "$utype",'uid' => "$uid"},
                                                       "tx" => {"$sum" => "$hr.tx_u"},
                                                       "rx" => {"$sum" => "$hr.rx_u"}}},
                                         {"$project" => {"tx" => 1,
                                                         "rx" => 1,
                                                         "total" => {"$add" => ["$tx", "$rx"]}}},
                                         {"$sort" => {"total" => -1}},
                                         {"$skip" => skip},
                                         {"$limit" => limit})
  end

  def find_criteria
    et = Date.today
    case query_for
    when "1d"
      st = et - 1.day
      fs = {"h" => "$hr.h", "d" => "$d"}
    when "2d"
      st = et - 2.days
      fs = {"h" => "$hr.h", "d" => "$d"}
    when "1w"
      st = et - 7.days
      fs = {"d" => "$d"}
    when "1m"
      st = et - 1.months
      fs = {"d" => "$d"}
    when "3m"
      st = et - 3.months
      fs = {"d" => "$d"}  #{"d" => {"$dayOfMonth" => "$d"}, "m" => {"$month" => "$d"}}
    when "6m"
      st = et - 6.months
      fs = {"d" => "$d"} #{"d" => {"$dayOfMonth" => "$d"}, "m" => {"$month" => "$d"}}
    else
      if query_for.is_a? Array
	st = Date.parse query_for[0].to_s
	et = Date.parse query_for[1].to_s
        #NOTE: change this 0 to 2 once if graph is fixed
	fs = (et - st).to_i > 2 ? {"d" => "$d"} : {"h" => "$hr.h", "d" => "$d"}
      else
	st = et - 1.day
	fs = {"h" => "$hr.h", "d" => "$d"}
      end
    end

    {st: st, et: et, group_criteria: fs}
  end
end
