require 'bcrypt'

require 'rolify/adapters/base'
require 'rolify/configure'
require 'rolify/dynamic'
require 'rolify/railtie' if defined?(Rails)
require 'rolify/resource'
require 'rolify/role'


class User < ActiveRecord::Base
  include BCrypt
  has_secure_password



  has_and_belongs_to_many :roles, :join_table => :users_roles
    
  validates_uniqueness_of :username, :allow_nil => true, :scope => :server_digest, :allow_blank => true

  attr_accessor :remember_token

  def is_admin?
    self.username == "admin"
  end  

  def self.retrieve_all
    if defined?(SERVER_DIGEST)
      User.where(:server_digest => SERVER_DIGEST)
    else
      User.all
    end     
  end


  def add_role(role_name, resource = nil)
    role = Role.where(:name => role_name, :resource_type =>  (resource.is_a?(Class) ? resource.to_s : resource.class.name if resource), :resource_id => (resource.id if resource && !resource.is_a?(Class)) ).first_or_create    

    if !roles.include?(role)
      self.class.define_dynamic_method(role_name, resource) if ::Rolify.dynamic_shortcuts
      self.roles << role

    end    
  end
  
  def has_role?(role_name, resource = nil)
    return has_strict_role?(role_name, resource) if resource and resource != :any

    if new_record?
      role_array = self.roles.detect { |r|
        r.name.to_s == role_name.to_s &&
          (r.resource == resource ||
           resource.nil? ||
           (resource == :any && r.resource.present?))
      }
    else
      role_array = self.roles.where(name: role_name, resource: resource)
    end
    return false if role_array.nil?
    role_array != []
  end

    

  def is_member?(resource)
    role_array = self.roles.where(resource: resource)
    is_admin? || role_array.count > 0
  end
  
  def remove_role(role_name, resource = nil)
    cond = { :name => role_name }
    cond[:resource_type] = (resource.is_a?(Class) ? resource.to_s : resource.class.name) if resource
    cond[:resource_id] = resource.id if resource && !resource.is_a?(Class)
    the_delete_roles = self.roles.where(cond)    
    if the_delete_roles
      self.roles.delete(the_delete_roles)
    end
    return the_delete_roles
  end
  
  def has_strict_role?(role_name, resource)
    where_strict(self.roles, name: role_name, resource: resource).any?
  end

  def where_strict(relation, args)
    return relation.where(:name => args[:name]) if args[:resource].blank?
    resource = if args[:resource].is_a?(Class)
                 {class: args[:resource].to_s, id: nil}
               else
                 {class: args[:resource].class.name, id: args[:resource].id}
               end

    relation.where(:name => args[:name], :resource_type => resource[:class], :resource_id => resource[:id])
  end
  

  def self.digest(string)
     cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine.cost
     BCrypt::Password.create(string, cost: cost)
  end


   def self.new_token
     SecureRandom.urlsafe_base64
   end
   

   def remember
     self.remember_token = User.new_token
     update_attribute(:remember_digest, User.digest(remember_token))
   end

   def forget
     update_attribute(:remember_digest, nil)
   end
   

   def authenticated?(remember_token)
     BCrypt::Password.new(remember_digest).is_password?(remember_token)
   end
      
end