class MonitoringController < ApplicationController
  include CacheExistence
  before_filter :authenticate_user!
  before_filter :dashboard_topbar#, :except => [:intelligence]
  before_filter :topbar_stats, :only => [:dashboard_refresh]
  layout "org_dash_layout", only: [:organization_dashboard]

  def query
    query_for = params[:query_for]
    query_type = params[:query_type]
    query_filters = params[:filters]
    return render json: {status: 400, message: "Please provide start & end timestamp."} if params[:filters].blank? || params[:filters][:start_time].blank? || params[:filters][:end_time].blank?
    case query_for
    when "client"
      res_data = LiveAggregateDataQuery.new(query_type, query_filters, @current_user).search_clients
    end 

    if res_data[:data].present?
      render json: res_data.merge({status: 200}), status: 200
    else
      render json: res_data.merge({status: 400}), status: 400
    end
  end

  def dashboard
    if current_user.role.name == 'inventory'
      redirect_to access_points_access_point_path
    end
      find_ssid_for_dashbaord
  end

  def find_ssid_for_dashbaord
    @network_ssids = @current_network.blank? ? [] : @current_network.network_ssids
  end

  def switch_ui
    if params["ui_name"].present?
      $redis.hset "current_ui", "#{current_user.id}", "#{params['ui_name']}"  
      render json: {status: "success", message: params["ui_name"]}
    else
      raw = $redis.hget "user_preferences", current_user.id
      preferences = raw.present? ? JSON.parse(raw) : {}
      if params[:preferences].present?
        if params[:preferences][:columns].present? && preferences["columns"].present?
          params[:preferences][:columns] = preferences["columns"].merge(params[:preferences][:columns])
        end
        preferences.merge!(params[:preferences])
        $redis.hset "user_preferences", current_user.id, preferences.to_json
      end
      render json: { status: "success", message: preferences }
    end
  end

  def dashboard_refresh
    if @current_network.present?
      router_inventories_macids = @router_inventories.reload.map(&:mac_id)
      if params[:history_date_range].present?
        from_time = Time.zone.parse(params[:history_date_range].split('|')[0]).to_i
        end_time = Time.zone.parse(params[:history_date_range].split('|')[1]).to_i
      else
        from_time = params[:last_refresh_at].present? ? params[:last_refresh_at].to_i : (Time.zone.now - @current_network.hb_freq * 20).to_i #1401467874
        end_time = params[:ends_at].to_i#Time.zone.now.to_i #1401689380
      end

      up_aps = RouterInventory.find_up_list(router_inventories_macids, RouterInventory.get_hb_intreval(@current_network.hb_freq))

      total_aps = router_inventories_macids.count
      aps_up_count = up_aps.count
      aps_down_count =  total_aps - aps_up_count
      notification_count = @current_network.network_unread_notifications.count
      # total_unread_notification_count = current_user.total_unread_notifications.count(:id)
      top_clients_usage = @current_network.top_clients_usage(router_inventories_macids, from_time, end_time)
      clients_device = top_clients_usage.group_by {|c| c["device"]}.map {|k,v| [k.to_s, v.count]}.sort_by {|c| c[0]}

      if params[:only_top_stats] == "true"
        output = {:from_time => from_time, :end_time => end_time, :total_aps => total_aps, :aps_up_count => aps_up_count, :aps_down_count => aps_down_count, :total_clients => top_clients_usage.count, :total_notification_count => notification_count}
      else
        top_aps_usage = @current_network.top_aps_usage(router_inventories_macids, from_time, end_time)
        aps_up = top_aps_usage.map {|a| a["_id"]}
        throughput = {}
        if params[:history_date_range].blank?
          if params[:last_refresh_at].blank?
            from = from_time
            while from <= end_time
              to = from + (@current_network.hb_freq * 2)
              throughput[to] = @current_network.network_throughput_for_interval(aps_up, from, to)
              from = to
            end
          else
            throughput[end_time] = @current_network.network_throughput_for_interval(aps_up, from_time, end_time)
          end

        else
          from = from_time
          while from <= end_time
            to = from + 1.days.seconds.to_i
            throughput[from] = @current_network.network_throughput_for_interval(aps_up, from, to, "$avg")
            from = to
          end
        end
        total_clients_per_ssid = @current_network.no_of_clients_count_per_ssids(aps_up, from_time, end_time)
        clients_mac_per_ap = @current_network.clients_per_aps(aps_up, from_time, end_time)
        clients_mac_per_ap = clients_mac_per_ap.inject({}) {|h, c| h.merge(c["_id"] => c["macs"])}
        total_clients_per_ssid = total_clients_per_ssid.inject({}) {|h, e| h.merge(e["_id"]["SSID"] => e["macs"].count)}

        # aps_list = @router_inventories.reload.inject({}) {|h, e| h.merge(e.mac_id => {"name" => e.name, "address" => e.formatted_address, "active" => up_aps.include?(e.mac_id), "public_ip" => e.get_public_ip, "clients_list" => clients_mac_per_ap[e.mac_id] || [], "url" => inventory_path(e.id), "lat" => e.latitude, "lng" => e.longitude})}
        h={}
        @router_inventories.reload.each do |e|
          h[e.mac_id] = {"name" => e.name, "address" => e.formatted_address, "active" => up_aps.include?(e.mac_id), "public_ip" => "", "clients_list" => clients_mac_per_ap[e.mac_id] || [], "url" => inventory_path(e.id), "lat" => e.latitude, "lng" => e.longitude}
        end
        aps_list = h
        output = {:top_aps => top_aps_usage[0,5], :top_clients => top_clients_usage[0,5], :from_time => from_time, :end_time => end_time, :total_aps => total_aps, :aps_up_count => aps_up_count, :aps_down_count => aps_down_count, :throughput => throughput, :total_clients => top_clients_usage.count, :aps_list => aps_list, :total_clients_per_ssid => total_clients_per_ssid, :total_notification_count => notification_count, :clients_device => clients_device}
      end
    else
      output = {}
    end
    render :json => output.to_json
  end

  def change_network
    network = current_user.location_networks.find_by_id(params[:id])
    unless network.present?
    network = current_user.location_networks.first
    flash[:notice] = "Your network has been changed to #{network.network_name}"
    end
    current_user.current_network = network.id
    @current_network = current_user.current_network
    logger.info("Current Network in show <<<<<<#{@current_network.id}") unless @current_network.blank?
    find_ssid_for_dashbaord
    if request.referer.include?("organization_dashboard")
      redirect_to home_path(@current_network)
    else
      redirect_to root_path
    end
  end

  def fetch_history_data
    query = params[:history_date_range].split('|')
    
    if params[:ap_mac].present?
      ris = [params[:ap_mac]]
      lns = [RouterInventory.find_by_mac_id(params[:ap_mac]).location_network_id]
    else
      @current_network = LocationNetwork.where(id: params[:network_id]).last unless params[:network_id].blank?
      render status: 404, json: { success: false, message: "Network not found." } and return if @current_network.blank?

      ris = @current_network.router_inventories.pluck(:mac_id)
      lns = [@current_network.id]
    end
    
    aggregate_query = AggregateDataQuery.new(query, ris, lns)
    output = {
      from: Time.parse(aggregate_query.criteria[:st].to_s).to_i,
      to: Time.parse(aggregate_query.criteria[:et].to_s).to_i
    }
    
    params[:query_for] = params[:query_for].split(',') if params[:query_for].is_a?(String)

    if params[:query_for].include?("ap-thro")
      throughput = aggregate_query.aps_throughput
      output["ap-t"] = throughput.inject({}) { |h, v| h[v["_id"]["d"].to_i + (v["_id"]["h"] || 0) * 3600] = { "tx" => v["tx"].round(2), "rx" => v["rx"].round(2) }; h }
      output["ap-u"] = throughput.inject({}) { |h, v| h[v["_id"]["d"].to_i + (v["_id"]["h"] || 0) * 3600] = { "tx_u" => v["tx_u"].round(2), "rx_u" => v["rx_u"].round(2) }; h }
    end

    if params[:query_for].include?("cli-thro")
      if params[:client_mac].present?
        throughput = aggregate_query.client_throughput(params[:client_mac])
      else
        throughput = aggregate_query.clients_throughput
      end
      output["cli-t"] = throughput.inject({}) { |h, v| h[v["_id"]["d"].to_i + (v["_id"]["h"] || 0) * 3600] = { "tx" => v["tx"].round(2), "rx" => v["rx"].round(2) }; h }
    end

    if params[:query_for].include?("ap-usage")
      top_aps_usage = aggregate_query.aps_total_usage(0, 5)
      output["ap-u"] = top_aps_usage.map { |ap| [ap['_id'], ap['tx'], ap['rx']] }.transpose
    end

    if params[:query_for].include?("cli-usage")
      hsh = {}
      val = ClientDevice.all.pluck(:mac, :nick_name, :original_name)
      val.each { |i| hsh[i[0]] = (i[1] || i[2].to_s) }
      
      if params[:query_from] == 'connected_clients'
        # Pagination setup
        page = (params[:page] || params[:offset] || 1).to_i
        per_page = (params[:per_page] || 10).to_i
        per_page = [[per_page, 1].max, 1000].min  # Ensure per_page is between 1 and 1000
        
        # offset
        offset = (page - 1) * per_page
        
        # total count using MongoDB aggregation directly
        count_result = ClientAggregateData.collection.aggregate(
          {"$match" => {"ap"=>{"$in"=> aggregate_query.router_inventories},
                        "ln" => {"$in" => aggregate_query.lns},
                        "d" => {"$gte"=> aggregate_query.criteria[:st], "$lte"=> aggregate_query.criteria[:et] }}},
          {"$project" => {"mac" => 1}},
          {"$group" => {"_id" => "$mac"}},
          {"$count" => "total"}
        )
        total_count = count_result.first ? count_result.first["total"] : 0
        
        total_pages = total_count > 0 ? (total_count.to_f / per_page).ceil : 1
        
        #paginated records
        top_clients_usage = aggregate_query.clients_total_usage(offset, per_page)
        current_page_count = top_clients_usage.count
        
        output["cli-u"] = top_clients_usage.map do |c|
          [
            c['_id'],
            c['tx'],
            c['rx'],
            c['ip'],
            (c['l_time'].present? ? Time.parse(c['l_time'].to_s).to_i * 1000 : ""),
            (hsh[c['_id']]) || (c['hostname'])
          ]
        end
        
        # pagination metadata
        output["meta"] = {
          per_page: per_page,
          count: current_page_count,
          current: page,
          previous: page > 1 ? page - 1 : nil,
          next: page < total_pages ? page + 1 : nil,
          pages: total_pages,
          total: total_count
        }
      else
        top_clients_usage = aggregate_query.clients_total_usage(0, 5)
        output["cli-u"] = top_clients_usage.map do |c|
          [
            c['_id'],
            c['tx'],
            c['rx'],
            c['ip'],
            (c['l_time'].present? ? Time.parse(c['l_time'].to_s).to_i * 1000 : ""),
            (hsh[c['_id']]) || (c['hostname'])
          ]
        end.transpose
      end
    end

    if params[:query_for].include?("con-clis")
      connected_clients = aggregate_query.clients_count
      output["con-clis"] = connected_clients.inject({}) { |h, v| h[v["_id"]["d"].to_i + (v["_id"]["h"] || 0) * 3600] = v["count"]; h }
    end

    if params[:query_for].include?("ssid-clis")
      ssids_clients = aggregate_query.clients_count_per_ssids
      output["ssid-clis"] = ssids_clients.inject({}) { |h, v| h[v["_id"].to_i] = v["count"]; h }
    end

    if params[:query_for].include?("clis-count")
      if params[:ap_mac].present?
        output["clis-count"] = aggregate_query.distinct_clients_count_for_ap(params[:ap_mac])
      else
        output["clis-count"] = aggregate_query.distinct_clients_count
      end
    end

    if params[:query_for].include?("clis-device")
      output["clis-device"] = aggregate_query.clients_devices_count
      output["clis-device"] = output["clis-device"].map { |c| [c['_id'], c['count']] }
    end

    if params[:query_for].include?("general")
      total_macs = LocationNetwork.includes(:router_inventories).where(:id => aggregate_query.lns).map { |ln| ln.router_inventories.pluck(:mac_id) }.flatten.uniq
      output["total_aps"] = total_macs.count
      output["up_aps"] = (total_macs & aggregate_query.up_aps).count
      output["total_clients"] = aggregate_query.distinct_clients_count
    end

    render json: output.to_json
  end

  def fetch_sites_details
    org = current_user.organisation
    org_id = org.id

    permitted_network_ids = nil
    unless current_user.is_user_allowed_all_networks?
      permitted_network_ids = current_user.get_all_permitted_networks_for_non_admin.pluck(:id)
    end

    total_networks = org.location_networks.joins(:router_inventories).where(id: permitted_network_ids.presence || org.location_networks.pluck(:id)).group('location_networks.id').having('COUNT(router_inventories.id) > 0').count.keys.count
  
    interval = "5m"
    health = AggregateSlotsData.fetch_health_data(org_id, interval,nil, permitted_network_ids)
    aggregated_data = AggregateSlotsData.sites_aggregate_data(org_id, interval, permitted_network_ids)
  
    result = aggregated_data.first || {}
  
    formatted_data = {
      from: result["_id"] || 0,
      online: result["online"] || 0,
      down: result["down"] || 0,
      imp: result["imp"] || 0,
      dor: result["dor"] || 0,
      total_networks: total_networks,
      health: health
    }
  
    render json: formatted_data
  end
  

  def fetch_quality_exp
    org = current_user.organisation
    org_id = org.id
    permitted_network_ids = nil
    unless current_user.is_user_allowed_all_networks?
      permitted_network_ids = current_user.get_all_permitted_networks_for_non_admin.pluck(:id)
    end
    total_networks = org.location_networks.joins(:router_inventories).where(id: permitted_network_ids.presence || org.location_networks.pluck(:id)).group('location_networks.id').having('COUNT(router_inventories.id) > 0').count.keys.count
    interval = "5m"
    reduced_qoe = AggregateSlotsData.compute_overall_health(org_id, interval, permitted_network_ids)
    aggregated_data = AggregateSlotsData.sites_aggregate_data(org_id, interval, permitted_network_ids)
    isp_outages = AggregateSlotsData.isp_outages_count(org_id, interval, permitted_network_ids)
  
    result = aggregated_data.first || {}
  
    formatted_data = {
      from: result["_id"] || 0,
      isp_outages: isp_outages,
      networks_affected: result["networks_affected"] || 0,
      managed_clients_count: result["managed_clients_count"] || 0,
      lan_count: result["lan_count"] || 0,
      wireless_count: result["wireless_count"] || 0,
      down_clients: result["down_clients"] || 0,
      total_networks: total_networks,
      reduced_qoe: reduced_qoe
    }
  
    render json: formatted_data
  end
  

  def fetch_hardware_details
    org_id = current_user.organisation.id
  
    interval = "5m"
    permitted_network_ids = nil
    unless current_user.is_user_allowed_all_networks?
      permitted_network_ids = current_user.get_all_permitted_networks_for_non_admin.pluck(:id)
    end
    aggregated_data = AggregateSlotsData.hardware_aggregate_data(org_id, interval, permitted_network_ids) || {}
  
    # Process Switch Models
    switch_models = (aggregated_data[:switch_model_totals] || []).map do |model|
      model_name = model.keys.first
      model_data = model[model_name] || {}
      {
        model_name => {
          total: model_data["total"].to_i,
          down: model_data["down"].to_i
        }
      }
    end
  
    switch_models.reject! { |m| m.values.first[:total].zero? && m.values.first[:down].zero? }
  
    # Process Access Point Models
    ap_models = (aggregated_data[:ap_model_totals] || []).map do |model|
      model_name = model.keys.first
      model_data = model[model_name] || {}
      {
        model_name => {
          total: model_data["total"].to_i,
          down: model_data["down"].to_i
        }
      }
    end
  
    ap_models.reject! { |m| m.values.first[:total].zero? && m.values.first[:down].zero? }
  
    formatted_data = {
      router: {
        total: aggregated_data[:router_down_count].to_i + aggregated_data[:router_up_count].to_i,
        down: aggregated_data[:router_down_count].to_i
      },
      switch: {
        total: aggregated_data[:switch_up_count].to_i + aggregated_data[:switch_down_count].to_i,
        down: aggregated_data[:switch_down_count].to_i,
        models: switch_models
      },
      access_point: {
        total: aggregated_data[:access_point_up_count].to_i + aggregated_data[:access_point_down_count].to_i,
        down: aggregated_data[:access_point_down_count].to_i,
        models: ap_models
      },
      total_uplink: aggregated_data[:uplink_total_count].to_i,
      total_cellular: aggregated_data[:cellular_total_count].to_i,
      enabled_ssid_count: aggregated_data[:enabled_ssid_count].to_i,
      wan_cell_count: aggregated_data[:wan_cell_count].to_i,
      avg_clients_count: aggregated_data[:avg_clients_count].to_i,
      lan_unused: aggregated_data[:lan_unused].to_i
    }
  
    render json: formatted_data
  end
    

  def fetch_org_history_data
    interval = "5m"
    org_id = current_user.organisation_id
    class_name = "Ln#{interval.capitalize}Data"
    model = Object.const_get(class_name)
    permitted_network_ids = nil
    unless current_user.is_user_allowed_all_networks?
      permitted_network_ids = current_user.get_all_permitted_networks_for_non_admin.pluck(:id)
    end
  
    # Pagination
    draw = params[:draw].to_i
    start = params[:start].to_i
    length = params[:length].to_i
    length = 10 if length.zero?
  
    search_query = params[:searchQuery].to_s.strip.downcase

    # Mapping for uplink status
    uplink_status_mapping = {
      "failed" => "inactive",
      "active" => "active",
      "ready" => "ready"
    }

    mapped_status = uplink_status_mapping[search_query]

  
    # Build base query for organization
    query = model.where(org_id: org_id)

    if permitted_network_ids.present?
      query = query.where(ln: {"$in" => permitted_network_ids})
    end

    if mapped_status.present?
        query_conditions = RouterInventory::UPLINK_TYPE.keys.map do |key|
          {
            "grouped_uplinks.#{key}" => {"$elemMatch" => {"$in" => [mapped_status]}}
          }
        end

        query = query.where(
          "$or" => query_conditions
        )
    else
      query = query.where(
        "$or" => [
          { network_name: /#{Regexp.escape(search_query)}/i },
          { template_name: /#{Regexp.escape(search_query)}/i },
          { isp: /#{Regexp.escape(search_query)}/i }
        ]
      )
    end

    if params[:order].present? && params[:order]["0"].present?
      column_index = params[:order]["0"][:column].to_i
      direction = params[:order]["0"][:dir] == 'asc' ? 1 : -1
  
      case column_index
      when 0 # Name column
        query = query.order("network_name" => direction)
      when 1 # Health column - sort by the highest health value (most recent)
        query = query.order("overall_health_last24hr" => -direction)
      when 2 # Router up, down, dor - prioritize the maximum count of up state
        query = query.order("device_details.router.up" => -direction)
      when 3 # Switch up, down, dor - prioritize the maximum count of up state
        query = query.order("device_details.switch.up" => -direction)
      when 4 # Access point up, down, dor - prioritize the maximum count of up state
        query = query.order("device_details.access_point.up" => -direction)
      when 5 # Uplink status - prioritize "active" and "primary"
        query = query.order("primary_uplink_status" => -direction)
      when 6
        query = query.order("isp" => direction)
      when 7 
        query = query.order("clients.count" => -direction)               
      when 8 
        query = query.order("template" => direction)
      else
        query = query.order(id: direction)
      end
    end

  
    # Apply pagination to the query
    aggregated_data = query.order({ id: 1 }).page((start/length) + 1).per(length)
  
    # Fetch total records count before applying pagination
    records_total = model.where(org_id: org_id)
    
    if permitted_network_ids.present?
      records_total = records_total.where(ln: {"$in" => permitted_network_ids})
    end

    records_total = records_total.count

    records_filtered = query.count

  
    filtered_entries = aggregated_data.map do |ln_data|
      ln_id = ln_data.ln
      health_data = AggregateSlotsData.fetch_health_data(org_id, "5m",  [ln_id], permitted_network_ids = nil)
  
      # Check device details for network types
      network_type = []
      dev_details = ln_data.device_details
      network_type << 'Router' if dev_details['router'].values.any? { |v| v > 0 }
      network_type << 'Switch' if dev_details['switch'].values.any? { |v| v > 0 }
      network_type << 'Access Point' if dev_details['access_point'].values.any? { |v| v > 0 }

      uplink_status = ln_data.uplink_info_array.inject(Hash.new) {|v, h| h[v.keys.first] = v.values.first;h}
  
      {
        name: ln_data["network_name"],
        health: health_data,
        router: dev_details['router'],
        switch: dev_details['switch'],
        access_point: dev_details['access_point'],
        network_type: network_type.join(', '),
        uplink_status: uplink_status,
        isp: ln_data["isp"],
        clients_wired: ln_data['clients']['wired'],
        clients_wireless: ln_data['clients']['wireless'],
        template: ln_data["template_name"],
        parent_id: ln_id
      }
    end
  
    output = {
      draw: draw,
      recordsTotal: records_total,
      recordsFiltered: records_filtered,
      data: filtered_entries
    }
  
    render json: output.to_json
  end
  
    
  def intelligence
    @orgs_dashboard = JSON.parse($redis.hget("AIOPS:DASHBOARD:MAP", current_user.organisation_id.to_s) || '{}')
    if current_user.email == "ankur@wavespot.net" 
      @ns = "wavespot"
    else
      @ns = "#{current_user.organisation_id}#{current_user.organisation.organisation_name.downcase.gsub(/[&|-|_| ]/, "")}"
    end
  end
    
  def fetch_realtime_throughput_data
    time_now = Time.zone.now
    end_time = time_now.to_i
    output = {}
    if params[:query_for].include? "cli-real-thro"
      from_time = (time_now - @current_network.hb_freq * 10).to_i #1401467874
      output = {from: from_time, to: end_time}
      throughput = {}
      from = from_time
      clients = {}
      ris = @current_network.router_inventories.pluck(:mac_id)
      while from < end_time
        to = from + (@current_network.hb_freq)
        clients = @current_network.clients_throughput_for_interval(ris, Time.at(from), Time.at(to), nil, params[:client_mac])
        throughput[to] = {rx: clients['rx'], tx: clients['tx']}
        clients = clients['clients']

        from = to
      end
      output["cli-t"] = throughput
      output["last_seen_clients"] = clients
    end

    if params[:query_for].include? "ap-real-thro"
      throughput = {}
      clients = []
      ris = []
      Rails.logger.info "Cuurent Organisation Userrrrrrrrrrrrrrrrr #{@current_user.email}"
      ri = @current_organisation.router_inventories.where(:mac_id => params[:ap_mac]).first
      if ri.present? && ri.location_network_id.present?
        ln = ri.location_network
        from_time = (time_now - (ln.try(:hb_freq) || 5) * 10).to_i #1401467874
        from = from_time
        output = {from: from_time, to: end_time}
        if(ri.switch?)
          ports_last_status = []
          ports_last_status = ri.get_ports_last_status Time.at(from_time)
        end
        #ln = ri.location_network
        while from < end_time
          to = from + ln.hb_freq
          ap_thro = ln.uplinks_throughput_for_interval([ri.mac_id], Time.at(from), Time.at(to))
          throughput[to] = ap_thro['uplinks']
          clients = ap_thro['clients']

          from = to
        end

        output["ap-t"] = throughput
        output["last_seen_clients"] = clients
        output["ports_last_status"] = ports_last_status unless ports_last_status.blank?
      end
    end
    render :json => output.to_json
  end

  private

  def current_ability
    if cache_exist?
      @current_ability ||= Ability::BaseAbility.new(current_user)
    else
      @current_ability ||= Ability.new(current_user)
    end
  end


  def topbar_stats
    @current_network = LocationNetwork.where(id: params[:network_id]).last unless params[:network_id].blank?
    @router_inventories = @current_network.blank? ? [] : @current_network.router_inventories
  end

  def organization_dashboard
    organization = current_user.organisation
    @networks = organization.location_networks
    render "monitoring/organization_dashboard"
  end
end