require 'bcrypt'

class User < ApplicationRecord
  attr_accessor :reset_token

  has_secure_password
  has_many :bugs
  has_many :projects
  has_many :favorite_projects
  has_many :favorite_bugs
  has_many :comments
  
  has_many :assigned_bugs,class_name: "Bug",foreign_key: :assigned_to_id
  EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
  USERNAME_REGEX = /\A[a-zA-Z0-9|\._]+\Z/
  FULLNAME_REGEX = /.+[a-zA-Z]{3}+/

  validates :username, :presence => true, :uniqueness => true, :format => {with: USERNAME_REGEX, message: "Invalid, (only A-Z, a-z, 0-9, '.' and '_' are acceptable)"}, :length => { :in => 3..20 }
  validates :email, :presence => true, :uniqueness => true, :format => EMAIL_REGEX
  validates :password, :confirmation => true, :length => { :in => 3..20 }
  validates :full_name, :presence => true, length: {:in => 3..60}, :format => FULLNAME_REGEX

  before_save :downcase_attributes , :remove_spaces
  before_create :downcase_attributes, :remove_spaces
  
  # Returns true if the given token matches the digest.
  def authenticated?(attribute, token)
    digest = send("#{attribute}_digest")
    return false if digest.nil?
    BCrypt::Password.new(digest).is_password?(token)
  end

  #Set password seset attributes
  def create_reset_digest
    self.reset_token = User.new_token
    update_attribute(:reset_digest,  User.digest(reset_token))
    update_attribute(:reset_sent_at, Time.zone.now)
  end

  def delete_reset_digest
    self.reset_token = nil
    update_attribute(:reset_digest, nil)
    update_attribute(:reset_sent_at, nil)
  end

  # Sends password reset email.
  def send_password_reset_email
    UserMailer.password_reset(self).deliver_now
  end

  # Returns true if a password reset has expired.
  def password_reset_expired?
    reset_sent_at.nil? || reset_sent_at < 30.minutes.ago
  end

  #Validate if the reset token digest exists and match the given token
  def valid_reset_token?(token)
    !self.reset_digest.nil? && !password_reset_expired? && self.authenticated?(:reset, token)
  end

  #Update password if token is valid and invalidate token after
  def update_password_with_token(password:,password_confirmation:,token:)
    status_update = false
    if self.valid_reset_token?(token)
      status_update = self.update_attributes(password: password,password_confirmation: password_confirmation)
        self.delete_reset_digest if status_update
    end
    status_update
  end

  def self.authenticate(username_or_email, password)
    if EMAIL_REGEX.match(username_or_email)
      user = User.find_by_email(username_or_email)
    else
      user = User.find_by_username(username_or_email)
    end
    if user && user.authenticate(password)
      return user
    else
      return false
    end
  end

  def add_favorite_bug(bug)
    return false unless bug.is_a? Bug
    if !bug_already_favorite?(bug)
      FavoriteBug.create(bug: bug, user: self)
      reload
      true
    else
      false
    end
  end

  def add_favorite_project(project)
    return false unless project.is_a? Project
    if !project_already_favorite?(project)
      FavoriteProject.create(project: project, user: self)
      reload
      true
    else
      false
    end
  end

  def bug_already_favorite?(bug)
    self.favorite_bugs.pluck(:bug_id).include? bug.id
  end

  def project_already_favorite?(project)
    self.favorite_projects.pluck(:project_id).include? project.id
  end

  def delete_all_favorite_bugs
    self.favorite_bugs.each(&:destroy)
  end

  def delete_all_favorite_projects
    self.favorite_projects.each(&:destroy)
  end
  def self.new_token
    SecureRandom.urlsafe_base64
  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.match_reset_token(email:, token:)
    user = User.find_by_email(email)
    if user&.valid_reset_token?(token)
      user
    end
  end

  def self.reset_token_exists?(token)
    User.pluck(:reset_digest).any?{ |digest| !digest.nil? && BCrypt::Password.new(digest).is_password?(token)}
  end

  def self.reset_token_is_valid?(token)
    User.any?{ |user| user.valid_reset_token?(token) && BCrypt::Password.new(user.reset_digest).is_password?(token)}
  end

  private

  def downcase_attributes
    self.email.downcase!
    self.username.downcase!
  end

  def remove_spaces
    self.full_name.strip!
  end
end
