# == Schema Information
#
# Table name: users 
#
#  id                     :integer          not null, primary key
#  email                  :string(255)      default(""), not null
#  encrypted_password     :string(255)      default(""), not null
#  reset_password_token   :string(255)
#  reset_password_sent_at :datetime
#  remember_created_at    :datetime
#  sign_in_count          :integer          default(0), not null
#  current_sign_in_at     :datetime
#  last_sign_in_at        :datetime
#  current_sign_in_ip     :string(255)
#  last_sign_in_ip        :string(255)
#  created_at             :datetime
#  updated_at             :datetime
#  full_name              :string(255)
#  phone_number           :string(255)
#  company_name           :string(255)
#  is_admin               :string(255)
#  organisation_id        :integer
#  organisation_access    :string(255)
#  confirmation_token     :string(255)
#  confirmed_at           :datetime
#  confirmation_sent_at   :datetime
#  unconfirmed_email      :string(255)
#  role_id                :integer
#  approved               :boolean          default(FALSE), not null
#  is_super_admin         :boolean          default(FALSE)
#  password_reset         :boolean          default(FALSE)
#

class User < ActiveRecord::Base
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  include PublicActivity::Model
  include UnityRelation
  acts_as_tagger

  tracked owner: ->(controller, model) { controller && controller.tracked_current_user },params: { :attributes=> proc {|controller, model_instance| { "users(#{model_instance.email})"=> model_instance.changes}}},organisation_id: ->(controller, model) { controller && controller.tracked_current_user.organisation_id if controller && controller.tracked_current_user}

  tracked assumed_by: proc {|controller, model| controller.user_assumed_by if controller}
  scope :machine_token_users, -> { where(is_admin: false, role_id: Role.api_role.id) }
  scope :not_machine_token_users, -> { where.not(role_id: Role.api_role.id) }
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable,:confirmable, :async, :saml_authenticatable

  attr_accessor :current_role
  attr_accessor :authorized_networks
  attr_accessor :from_sa
  acts_as_taggable_on :tags

  MODEL_NAME_MAP = [ 'access_token', 'confirmation_token', 'confirmed_at', 'confirmation_sent_at', 'organisation_access', 'is_admin']

  serialize :network_tags, Hash
  belongs_to :role
  belongs_to :organisation
  has_many :assume_user_details
  has_many :unities, dependent: :destroy
  #has_many :sim_managements, dependent: :destroy
  #has_many :purchase_orders, dependent: :destroy
  #has_many :payment_invoices, dependent: :destroy
  #has_many :sim_payment, dependent: :destroy
  has_many :init_templates, dependent: :destroy
  has_many :networks_with_limited_access, -> { where("unities.permission = 'limited'") }, through: :unities, class_name: "LocationNetwork", source: :location_network
  has_many :networks_with_full_access, -> { where("unities.permission = 'full'") }, through: :unities, class_name: "LocationNetwork", source: :location_network
  #has_many :networks_with_write, through: :unities
  has_and_belongs_to_many :notification_groups, join_table: :users_notification_groups

  ["network_ssids", "wired_configs", "reports", "ntp_servers", "alerts", "firewall_configs", "loggings", "vpn_configs", "switch_configurations"].each do |model|
    ["limited", "full"].each do |access|
      define_method("#{model}_#{access}") do
        model.singularize.camelize.safe_constantize.where(location_network_id: network_acc_to_permission(access).uniq)
      end
    end
  end

  def network_acc_to_permission(access)
    if self.is_admin? || self.is_super_admin || self.machine_token_user? || (self.network_tags[:full] && self.network_tags[:full].include?("all:all") && access == 'full') || (self.network_tags[:limited] && self.network_tags[:limited].include?("all:all") && access == 'limited')
      self.organisation.location_networks.pluck(:id)
    else
      self.organisation.location_networks.tagged_with(self.unities.where(locatable_type: "ActsAsTaggableOn::Tag", permission: access).flat_map { |unity| unity.locatable.name}).pluck(:id) + self.unities.where(locatable_type: 'LocationNetwork', permission: access).pluck(:locatable_id)
    end
  end

  before_validation :set_organisation_id, :on => :create
  accepts_nested_attributes_for :organisation

  before_save :remove_space_phone_number
  before_create :generate_access_token
  after_create :clone_router_inventory               #cloning router inventory to user(current_user)
  validate :validate_router_inventory, on: :create

  before_update do |x|
    changes = x.changed - ["updated_at"]
    unless changes.present?
      User.public_activity_off 
      true
    else
      User.public_activity_on 
    end
  end

  def generate_access_token
    length = 32
    self.access_token = SecureRandom.hex(length)
    generate_access_token if User.exists?(access_token: self.access_token)
  end

  def machine_token_user?
    self.role_id.eql? Role.api_role.id
  end

  #Cloning the router_inventory to user from master account after sign_up
  def clone_router_inventory 
    if !self.is_super_admin? && !self.claimed_devices.blank?
      devices = (self.claimed_devices.gsub(/\s+/, "")).split(',')
      devices.each do |device|
        router_obj = RouterInventory.where(mac_id: device).first
        RouterInventory.auto_provisioning_ap(router_obj,nil,self) if router_obj.present?
      end
    end
  end 
  
  def remove_space_phone_number
    self.phone_number = "#{(self.phone_number || "").gsub(/\s/, '')}"
  end

  after_create do |user|
    user.confirm
    user.approved = true    
  end

  def current_network
    current_network = LocationNetwork.find_by_id($redis.hget "USER:CURRENT_NETWORK", "#{self.id}")
    current_network = self.organisation.location_networks.first if current_network.blank?
    return current_network
  end

  def current_network=(id)
    location = self.organisation.location_networks.find_by_id(id)
    return nil if location.blank?
    $redis.hset "USER:CURRENT_NETWORK", "#{self.id}", "#{location.id}"
    return location
  end

  def json_build
    {id: self.id, full_name: self.full_name, user_email: self.email, phone_number: self.phone_number, company_name: self.company_name, organisation_id: self.organisation_id, confirmation_token: self.confirmation_token, role_id: self.role_id, approved: self.approved, access_token: self.access_token, authorized_networks: self.network_tags, claimed_devices: self.claimed_devices}
  end

  def has_role? role_name
    self.role.try(:name) == role_name.to_s
  end

  def is_admin?
    self.role.try(:name) == "admin"
  end

  def is_network_admin?
    self.role.try(:name) == "network_admin"
  end

  def is_guest?
    self.role.try(:name) == "guest"
  end

  def network_access_values 
    check = self.network_tags.keys
    val = self.network_tags
    hash = {"full" => {}, "limited" => {}}
    if check.include?("full")
      hsh = {}
      tags, locations = val["full"].partition{|i| i.split(':').include?("Tag") }
      hsh["tags"] = tags.map{|i| i.split(':').last}
      hsh["all"] = true if locations.delete("all:all") if locations.present?
      hsh["locations"] = LocationNetwork.where(:id => locations.map{|i| i.split(':').last}).pluck(:id) if locations.present?
      hash["full"] = hsh
    end
    if check.include?("limited")
      hsh1 = {}
      l_tags, l_locations = val["limited"].partition{|i| i.split(':').include?("Tag") }
      hsh1["tags"] = l_tags.map{|i| i.split(':').last}
      hsh1["all"] = true if l_locations.delete("all:all") if l_locations.present?
      hsh1["locations"] = LocationNetwork.where(:id => l_locations.map{|i| i.split(':').last}).pluck(:id) if l_locations.present?
      hash["limited"] = hsh1
    end    
    hash
  end  

  #Getter method for setting unities
  # def authorized_networks
  #   { "limited" =>  self.unities.only_limited_permission(self.role_id).pluck(:location_network_id).sort,
  #     "full" => self.unities.only_full_permission(self.role_id).pluck(:location_network_id).sort }
  # end

  #Setter method for setting unities
  def authorized_networks=(value)
    if self.is_admin? || self.is_super_admin || self.machine_token_user? || (value[:full] && value[:full].include?("all:all")) || (value[:limited] && value[:limited].include?("all:all"))
      self.unities.destroy_all
      return
    end
    unity = []
    full_access = value[:full] || []
    limited_access = value[:limited] || []

    if self.role_id_changed?
      self.unities.destroy_all
      limited_access.each do |locatable|
        if locatable.include?('Tag')
          unity << {locatable_id: ActsAsTaggableOn::Tag.find_by_name(locatable.split(':')[1]).try(:id), role_id: self.role_id, permission: 'limited', locatable_type: "ActsAsTaggableOn::Tag"}
        else
          unity << {locatable_id: locatable.split(':')[1], role_id: self.role_id, permission: 'limited', locatable_type: "LocationNetwork"}
        end
      end

      if self.is_network_admin?
        full_access.each do |locatable|
          if locatable.include?('Tag')
            unity << {locatable_id: ActsAsTaggableOn::Tag.find_by_name(locatable.split(':')[1]).try(:id), role_id: self.role_id, permission: 'full', locatable_type: "ActsAsTaggableOn::Tag"}
          else
            unity << {locatable_id: locatable.split(':')[1], role_id: self.role_id, permission: 'full', locatable_type: "LocationNetwork"}
          end
        end
      end
      #raise "User is not admin and role changed"
    else
      self.unities.destroy_all
      limited_access.each do |locatable|
        if locatable.include?('Tag')
          unity << {locatable_id: ActsAsTaggableOn::Tag.find_by_name(locatable.split(':')[1]).try(:id), role_id: self.role_id, permission: 'limited', locatable_type: "ActsAsTaggableOn::Tag"}
        else
          unity << {locatable_id: locatable.split(':')[1], role_id: self.role_id, permission: 'limited', locatable_type: "LocationNetwork"}
        end
      end
      if self.is_network_admin?
        self.unities.destroy_all
        full_access.each do |locatable|
          if locatable.include?('Tag')
            unity << {locatable_id: ActsAsTaggableOn::Tag.find_by_name(locatable.split(':')[1]).try(:id), role_id: self.role_id, permission: 'full', locatable_type: "ActsAsTaggableOn::Tag"}
          else
            unity << {locatable_id: locatable.split(':')[1], role_id: self.role_id, permission: 'full', locatable_type: "LocationNetwork"}
          end
        end
      end
      #raise "User is not admin and role not changed"
    end
    self.unities.build unity unless unity.blank?
  end

  def aps_inventory_tags
    tag_list = []; network_list = []; ap_list = []
    self.router_inventories_full.each do |x|
      ap_list << {"id" => x.id.to_s + "--inventory", "name" => x.mac_id}
      x.tag_list.each {|y| tag_list << {"id" => y + "--tag_list", "name" => y} }
    end
    self.networks_with_full_access.each do |y|
      network_list << {"id" => y.id.to_s + "--network_name", "name" => y.network_name}
    end

    return tag_list.flatten.uniq + network_list + ap_list
  end

  def active_for_authentication?
    super && approved?
  end

  def inactive_message
    unless confirmed_at.blank?
      approved? ? super : :not_approved
    else
      :not_verified
    end
  end

  def total_unread_notifications
    Notification.where(:is_read => false, :ap_mac_id.in => self.router_inventories.map(&:mac_id)).desc("created_at")
  end

  def self.total_read_notifications
    Notification.where(:is_read => true, :ap_mac_id.in => self.router_inventories.map(&:mac_id)).desc("created_at")
  end

  # Override Devise::Confirmable#after_confirmation
  def after_confirmation
    super
    if self.confirmed_at
      self.update_column :approved, true
    end
  end


  private

  def validate_router_inventory
    if self.claimed_devices && (self.claimed_devices.split(',')).present?
      invalid_mac_ids = RouterInventory.where(mac_id: (self.claimed_devices).split(',')).pluck(:mac_id)
      errors.add(:claimed_devices, ": Invalid mac_id #{self.claimed_devices}") if (invalid_mac_ids.empty?)
      already_claimed_devices = RouterInventory.where(mac_id: self.claimed_devices.split(','), is_auto_provisioned: false)
      errors.add(:claimed_devices, "provided mac_id #{already_claimed_devices.map{|item| %Q{"#{item.mac_id}"}}.join(', ')} are already claimed") if
      !(already_claimed_devices.empty?)
    end
  end
  

  def set_organisation_id
    if !self.from_sa
      if self.is_admin.eql?("true")
        self.organisation_attributes = { :organisation_name => self.company_name }
        self.organisation_access = "full"
      else
        self.organisation_access = "limited"
        self.is_admin = "false"
      end
    else
      self.organisation_access = "full"
      self.is_admin = "true"
      self.role_id = Role.where(:name => 'admin').first.id
      self.approved = true
      self.confirmed_at = Time.now
    end
    Rails.logger.info "<>>>>>>>>>>>>>>>>>>>>>>#{self.id}"
    Rails.logger.info "<>>>>>>>>>>>>>>>>>>>>>>#{self.organisation_access}"
  end
end
