# == Schema Information
#
# Table name: alerts
#
#  id                  :integer          not null, primary key
#  name                :string(255)
#  message             :text
#  category            :string(255)
#  severity_color      :string(255)
#  created_at          :datetime
#  updated_at          :datetime
#  location_network_id :integer
#  actions             :text
#

class Alert < ActiveRecord::Base
  resourcify
  include ActionView::Helpers::NumberHelper
  extend RedisWrapper
  include PublicActivity::Model

  tracked owner: ->(controller, model) { controller && controller.tracked_current_user },params: { :attributes => proc {|controller, model_instance| {"alert(#{model_instance.name})" => model_instance.changes}}},organisation_id: ->(controller, model) { controller && controller.tracked_current_user.try(:organisation_id) },location_network_id: proc {|controller, model_instance| model_instance.location_network_id}
  tracked assumed_by: proc {|controller, model| controller.user_assumed_by if controller}


  ACTIONS_LIST = ["Email"]
  ALERT_EVENT_TYPE = {"associated" => "35" ,"disassociated" => "36" ,'login' =>  "38" ,'logout' => "39",'session_timeout' => '37', "authenticated" => '33', "deauthenticated" => '33','uplink_up_down' => '1','scan_done' => '68','dfs' => '67','radar' => '66'}
  belongs_to :location_network
  has_many :alert_rules, :dependent => :destroy
  has_and_belongs_to_many :notification_groups, join_table: :alerts_notification_groups

  accepts_nested_attributes_for :alert_rules

  serialize :actions, Array

  acts_as_taggable

  after_save do |alert|
    ln = alert.location_network
    update_redis_alerts(ln)
  end

  after_destroy do |alert|
    if alert.location_network.alerts.blank?
      ri = alert.location_network.router_inventories.pluck(:mac_id)
      ri.each{|x| $redis.hset "AP:#{x}","alert","0"}
    else	
      $redis.del "alert:#{self.id}"	
      $redis.hmset "network:#{self.location_network_id}","alerts","#{self.location_network.alerts.pluck(:id)}"
    end
  end

  def send_webhook_event event, payload
    self.notification_groups.each do |notification_group|
      WebhookEventWorker.perform_async(notification_group, event, payload, self.location_network_id, self.location_network.organisation_id)
    end
  end
  
  def update_redis_alerts(ln)
    ri = ln.router_inventories.pluck(:mac_id)
    ri.each{|x| $redis.hset "AP:#{x}","alert","1"}

    alert_ids = ln.alerts.includes(:alert_rules).where.not(alert_rules: {alert_on: "Client"}).pluck(:id)
    $redis.hmset "network:#{ln.id}","alerts","#{alert_ids}"
    
    rules = self.alert_rules.map(&:attributes) #TODO: Remove each one from this condition once it gets fixed
    #rules = self.alert_rules.where.not(:alert_on => ["Ethernet throughput", "SSID throughput", "Client throughput", "Connected clients count", "Load average", "Free memory"]).map(&:attributes) #TODO: Remove each one from this condition once it gets fixed
    $redis.hset "alert:#{self.id}","rules",rules.to_json
  end

  def notification_message(text_msg, data,network_name)
    text_msg.gsub("{ap_mac}", data[:ap_mac_id] || '').gsub("{client_mac}", data[:client_mac_id] || '').gsub("{value}", data[:value].to_s || '').gsub("{ssid}", data[:ssid] || '').gsub("{condition}", data[:condition] || '').gsub("{uplink_name}", data[:uplink_name] || '').gsub("{network_name}", network_name || '').gsub("{device_name}", data[:ap_name] || '') if text_msg.present?
  end

  def notify? data, data_at, is_test=false
    rules = $redis.hget "alert:#{self.id}", "rules"
    data_at = Time.zone.at data_at
    stat_alerts = ["Device", "Captive portal", "Network uplink","Client"]
    alert_rules = JSON.parse(rules || "[]")	
    alert_rules.each do |rule|	
      rule = AlertRule.new(rule)
      next if data["INTERFACE_STATS"].blank? && !stat_alerts.include?(rule.alert_on)
      case rule.alert_on
      when "Device"
        if rule.condition == "up"
          return send_test_notification({ap_mac_id: "XX:XX:XX:XX:XX:XX"}) unless !is_test
          # mc = $redis.hget "AP:#{data["INFO"]['NASID']}", "prev_last_hb"
          # hb_interval = self.location_network.hb_freq
          ap_details = $redis.hgetall "AP:#{data["INFO"]['NASID']}"
          mc = ap_details['prev_last_hb']# = $redis.hgetall "AP:#{data["INFO"]['NASID']}", "prev_last_hb"
          hb_interval = ap_details['hb_freq'] || 5 #self.location_network.hb_freq
          hb_interval = rule.value.to_i > hb_interval.to_i * 2 ?  rule.value.to_i : hb_interval.to_i * 2  
          mc = mc.to_i unless mc.blank?
          if mc.blank? || (data_at - mc).to_i > hb_interval
            send_notification({ap_mac_id: data["INFO"]["NASID"].upcase}) unless (ap_details["hw_type"] == "switch")
          end
        elsif rule.condition == "down"
          return send_test_notification({ap_mac_id: "XX:XX:XX:XX:XX:XX"}) unless !is_test
          if find_exisitng_notification(data["INFO"]["NASID"].upcase).count > 0
            reset_notification(data["INFO"]["NASID"].upcase)
          end
          if($redis.hget("LN:DOWN:ALERT:RUN:STATUS", self.location_network_id) != "1")
            $redis.hset("LN:DOWN:ALERT:RUN:STATUS", self.location_network_id, "1")
            devices = self.location_network.router_inventories.pluck(:mac_id)
            down_interval = $redis.hget "AP:#{data["INFO"]['NASID']}", "down_interval"
            up_devices = RouterInventory.find_up_list(devices, down_interval || rule.value.to_i)
            down_devices = devices - up_devices
            unless down_devices.blank?
              down_devices.each do |ap_mac|
                next if ($redis.hget "AP:#{ap_mac.upcase}", "hw_type") == "switch"
                send_notification({ap_mac_id: ap_mac}) unless find_exisitng_notification(ap_mac).count > 0
              end
            end
            $redis.hset("LN:DOWN:ALERT:RUN:STATUS", self.location_network_id, "0")
            if($redis.hget("LN:DOWN:ALERT:ATTACHEDNETWORK", self.location_network_id))
              network_list = JSON.parse($redis.hget("LN:DOWN:ALERT:ATTACHEDNETWORK", self.location_network_id))
              network_list.each do |network|
                Rails.logger.info "------------------ IN ATTACHEDNETWORK #{network} --------------"
                alerts = Alert.includes(:alert_rules).where(:location_network_id => network.to_i, :alert_rules => {:alert_on => 'Device', :condition => 'down
    '})
                alerts.each do |alert|
                  if($redis.hget("LN:DOWN:ALERT:RUN:STATUS", alert.location_network_id) != "1")
                    $redis.hset("LN:DOWN:ALERT:RUN:STATUS", alert.location_network_id, "1")
                    ln = alert.location_network
                    next if ln.blank?
                    devices = ln.router_inventories.pluck(:mac_id)
                    a_rules = $redis.hget "alert:#{alert.id}", "rules"
                    next if a_rules.blank?
                    up_devices = RouterInventory.find_up_list(devices, AlertRule.new(JSON.parse(a_rules).first).value.to_i)
                    down_devices = devices - up_devices
                    unless down_devices.blank?
                      down_devices.each do |ap_mac|
                        alert.send_notification({ap_mac_id: ap_mac}) unless alert.find_exisitng_notification(ap_mac).count > 0
                      end
                    end
                    $redis.hset("LN:DOWN:ALERT:RUN:STATUS", alert.location_network_id, "0")
                  end
                end
              end
            end
          end
        elsif rule.condition == "reboot"
          return send_test_notification({ap_mac_id: "XX:XX:XX:XX:XX:XX"}) unless !is_test
          if ($redis.hget "AP:#{data["INFO"]["NASID"].upcase}", "hw_type") != "switch" && data['INFO']['FIRST_HB'] == 1 #NOTE:Remove condition for switch when we fix the issue on TIMESTAMP
            send_notification({ap_mac_id: data["INFO"]["NASID"].upcase})
          end
        elsif rule.condition == "upgrade"
          return send_test_notification({ap_mac_id: "XX:XX:XX:XX:XX:XX"}) unless !is_test
          redis = Alert.redis_hgetall "AP:#{data['INFO']['NASID']}"
          if (!redis['upgrade'].blank? && redis['upgrade'] != "0" && data['INFO']['PIAP_VERSION'] != redis['p_version']) && (data['INFO']['FIRST_HB'].blank? || data['INFO']['FIRST_HB'] == 1)
            upgrade = Upgrade.find(redis['upgrade'])
            upgrade.update column(:status ,"Success")
            Alert.redis_hset "AP:#{data['INFO']['NASID']}",'upgrade','0'
            send_notification({ap_mac_id: data["INFO"]["NASID"].upcase})
          end
        end
      when "Ethernet throughput"
        return send_test_notification({ap_mac_id: "XX:XX:XX:XX:XX:XX"}) unless !is_test
        lan_tx = data["INTERFACE_STATS"]["UPLINK"].present? ? (data["INTERFACE_STATS"]["UPLINK"].is_a?(Array) ? (data["INTERFACE_STATS"]["UPLINK"].sum {|t| t['TX_RATE'].to_i}) : data["INTERFACE_STATS"]["UPLINK"]["TX_RATE"]) : data["INTERFACE_STATS"]["LAN"]["TX_RATE"]
        lan_rx = data["INTERFACE_STATS"]["UPLINK"].present? ? (data["INTERFACE_STATS"]["UPLINK"].is_a?(Array) ? (data["INTERFACE_STATS"]["UPLINK"].sum {|t| t['RX_RATE'].to_i}) : data["INTERFACE_STATS"]["UPLINK"]["RX_RATE"]) : data["INTERFACE_STATS"]["LAN"]["RX_RATE"]
        value = (lan_tx + lan_rx) / 1024
        if rule.check_rule_condition(value)
          send_notification({ap_mac_id: data["INFO"]["NASID"].upcase, value: value}) unless find_exisitng_notification(data["INFO"]["NASID"].upcase).count > 0
        else
          reset_notification(data["INFO"]["NASID"].upcase)
        end
      when "SSID throughput"
        return send_test_notification({ap_mac_id: "XX:XX:XX:XX:XX:XX", ssid: "SSID_NAME"}) unless !is_test
        data["INTERFACE_STATS"].each do |k, v|
          ssid = NetworkSsid.find_by(id: k)
          value = (v["TX_RATE"] + v["RX_RATE"]) / 1024
          ssid.present? && rule.check_rule_condition(value) ? send_notification({ap_mac_id: data["INFO"]["NASID"].upcase, ssid: ssid_name, value: value}) : nil
        end
      when "Free memory"
        return send_test_notification({ap_mac_id: "XX:XX:XX:XX:XX:XX"}) unless !is_test
        if !(data["MEMINFO"]["FREE"]).blank? && !(data["MEMINFO"]["TOTAL"]).blank?
          value = ((data["MEMINFO"]["FREE"]).class == String ? ((data["MEMINFO"]["FREE"].gsub('kB', '').to_f / data["MEMINFO"]["TOTAL"].gsub('kB', '').to_f) * 100).round : ((data["MEMINFO"]["FREE"].to_f / data["MEMINFO"]["TOTAL"].to_f) * 100).round)
          if rule.check_rule_condition(value)
            send_notification({ap_mac_id: data["INFO"]["NASID"].upcase, value: value}) unless find_exisitng_notification(data["INFO"]["NASID"].upcase).count > 0
          else
            reset_notification(data["INFO"]["NASID"].upcase)
          end
        end        
      when "Client throughput"
        return send_test_notification({ap_mac_id: "XX:XX:XX:XX:XX:XX", client_mac_id: "XX:XX:XX:XX:XX:XX"}) unless !is_test
        data["WLAN_CLIENTS"].each do |v|
          value = (v["TX_BYTES"] + v["RX_BYTES"]) / 1024
          rule.check_rule_condition(value) ? send_notification({ap_mac_id: data["INFO"]["NASID"].upcase, value: value, client_mac_id: v["MAC"].upcase}) : nil
        end
      when "Connected clients count"
        return send_test_notification({ap_mac_id: "XX:XX:XX:XX:XX:XX"}) unless !is_test
        value = data["WLAN_CLIENTS"].count
        if rule.check_rule_condition(value)
          send_notification({ap_mac_id: data["INFO"]["NASID"].upcase, value: value}) unless find_exisitng_notification(data["INFO"]["NASID"].upcase).count > 0
        else
          reset_notification(data["INFO"]["NASID"].upcase)
        end
      when "Load average"
        return send_test_notification({ap_mac_id: "XX:XX:XX:XX:XX:XX"}) unless !is_test
        unless data["SYSTEM_INFO"]["LOAD_AVG"].blank?
          value = data["SYSTEM_INFO"]["LOAD_AVG"].split(" ")[0].to_f
          if rule.check_rule_condition(value)
            send_notification({ap_mac_id: data["INFO"]["NASID"].upcase, value: value}) unless find_exisitng_notification(data["INFO"]["NASID"].upcase).count > 0
          else
            reset_notification(data["INFO"]["NASID"].upcase)
          end
        end
      when "Client"
        return send_test_notification({ap_mac_id: "XX:XX:XX:XX:XX:XX", client_mac_id: "XX:XX:XX:XX:XX:XX", ssid: "SSID_NAME"}) unless !is_test
        # mc = MonitoringChild.where("info.NASID" => data["INFO"]["NASID"].upcase, "created_at" => {"$lt" => data_at}).desc("created_at").first
        # if mc.present?
        #   prev_cli = mc.clients.map {|c| [c["MAC"], c["SSID"]]}
        # else
        #   prev_cli = []
        # end
        # curr_cli = data["WLAN_CLIENTS"].map {|c| [c["MAC"], c["SSID"]]}
        # new_clients = []
        if rule.condition == 'associated'
          # new_clients = curr_cli - prev_cli
          send_notification({ap_mac_id: data["NASID"].upcase, client_mac_id: data["DATA"]["CLI_MAC"].upcase, ssid: data["DATA"]["SSID"]}) unless data["DATA"].blank? || data["DATA"]["SSID"].blank?
        elsif rule.condition == 'disassociated'
          # new_clients = prev_cli - curr_cli
          send_notification({ap_mac_id: data["NASID"].upcase, client_mac_id: (data["DATA"]||{})["CLI_MAC"].upcase, ssid: data["DATA"]["SSID"]}) unless data["DATA"].blank? || data["DATA"]["SSID"].blank?
        end
        # new_clients.each do |v|
        #   send_notification({ap_mac_id: data["INFO"]["NASID"].upcase, client_mac_id: v[0].upcase, ssid: v[1]})
        # end
      when "Captive portal"
        return send_test_notification({ap_mac_id: "XX:XX:XX:XX:XX:XX", client_mac_id: "XX:XX:XX:XX:XX:XX"}) unless !is_test
        unless data["NASID"].blank?
          send_notification({ap_mac_id: data["NASID"].upcase, client_mac_id: data["MACID"].upcase, extra_data: data})
        end
      when "Network uplink"
        return send_test_notification({ap_mac_id: "XX:XX:XX:XX:XX:XX", uplink_name: "UPLINK_NAME"}) unless !is_test
        if data["DATA"].present?
          if rule.condition == "up"
            name = Uplink.where(:id => data["DATA"]["UID"]).first.try(&:uplink_name)
            send_notification({ap_mac_id: data["NASID"].upcase, extra_data: data, uplink_name: name ,condition: rule.condition,alert_on:rule.alert_on})
          elsif rule.condition == "down"
            name = Uplink.where(:id => data["DATA"]["UID"]).first.try(&:uplink_name)
            send_notification({ap_mac_id: data["NASID"].upcase, extra_data: data, uplink_name: name,condition: rule.condition,alert_on:rule.alert_on})
          elsif rule.condition == "uplink_switch_over"
            name = Uplink.where(:id => data["DATA"]["UID"]).first.try(&:uplink_name)
            send_notification({ap_mac_id: data["NASID"].upcase, extra_data: data, uplink_name: name, condition: rule.condition, alert_on:rule.alert_on})
          end
        end  
      end
    end
  end

  def send_notification data
    r_data = $redis.hgetall "AP:#{data[:ap_mac_id]}"
    network_name = r_data["Network"] || ""
    data[:ap_name] = r_data['AP'] || ""
    if r_data['alert_disable'] != '1'
      notification = self.initialize_notification data, network_name
      notification.save
      NotificationGroup.delay.send_notification({object_id: self.id,type: "Alert", notification_id: notification.id,network_name: network_name})
    end
  end

  def send_test_notification data
    notification = self.initialize_notification data, self.location_network.network_name
    NotificationGroup.send_test_webhook_alert self, notification
  end

  def initialize_notification data, network_name
    Notification.new(:alert_id => self.id, :location_network_id => self.location_network_id,:alert_category => self.category, :alert_tags => self.tag_list, :severity_color => self.severity_color,:subject => self.notification_message(self.subject, data, network_name), :message => self.notification_message(self.message, data, network_name), :created_at => Time.zone.now.utc, :is_sent => true, :is_flagged => true, :ap_mac_id => data[:ap_mac_id], :client_mac_id => data[:client_mac_id], :ssid => data[:ssid], :extra_data =>  data.stringify_keys)
  end

  def reset_notification data
    find_exisitng_notification(data).update(is_flagged: false)
  end

  def find_exisitng_notification ap_mac
    Notification.where(is_flagged: true, alert_id: self.id, ap_mac_id: ap_mac)
  end

  def notify_activity_alert? data
    NotificationGroup.send_notification({ object_id: self.id, type: "Alert", activity_id: data[:id], payload: data })
  end
end
