# == Schema Information
#
# Table name: authentication_tokens
#
#  created_at   :datetime
#  expires_at   :datetime
#  hashed_token :string(255)
#  id           :integer          not null, primary key
#  ip_address   :string(255)
#  updated_at   :datetime
#  user_agent   :string(255)
#
# Indexes
#
#  index_authentication_tokens_on_hashed_token  (hashed_token)
#

require 'bcrypt' unless defined? BCrypt
class AuthenticationToken < ActiveRecord::Base

  has_one :user

  attr_accessor :client_token

  after_initialize :adjust_bcrypt_cost_for_environment
  after_initialize :generate_token

  def self.verified(token)
    return false if token.nil? or token.blank?

    parts   = token.to_s.split(':')
    return false if parts.length < 3

    uuid    = parts.first
    salt    = parts.second
    secret  = parts.last
    return false unless valid_salt?(salt)
    return false unless (uuid and salt and secret)

    hash    = BCrypt::Engine.hash_secret(peppered(secret), salt)
    verified_token = self.find_by_hashed_token(token_format([uuid, salt, hash]))


    return false unless verified_token

    if verified_token.expired?
      # log the access attempt
      return false
    else
      verified_token.set_expiration_date and verified_token.save
    end

    return verified_token
  end

  def expired?
    not self.expires_at.future?
  end

  def generate_token
    unless persisted?
      uuid   = SecureRandom.uuid.gsub('-','').to_s
      secret = SecureRandom.hex(64).to_s
      hash   = BCrypt::Password.create(peppered(secret), cost: @cost)

      self.client_token = token_format [uuid, hash.salt, secret]  # give to client
      self.hashed_token = token_format [uuid, hash.salt, hash]    # store hashed
      set_expiration_date
    end
  end

  def set_expiration_date
    self.expires_at = 10.hours.from_now
  end


  ################################################################
  ################################################################
  private

  def self.valid_salt?(salt)
    !!(salt =~ /^\$[0-9a-z]{2,}\$[0-9]{2,}\$[A-Za-z0-9\.\/]{22,}$/)
  end

  def persisted?
    self.id ? true : false
  end

  def self.token_format(parts = [])
    parts.join(':')
  end

  def token_format(parts)
    self.class.token_format(parts)
  end

  def self.peppered(secret)
    "#{secret}#{Devise.secret_key}"
  end

  def peppered(secret)
    self.class.peppered(secret)
  end

  def adjust_bcrypt_cost_for_environment
    # a value 10 or higher is needed for production security, but we want fast tests
    @cost ||= ( Rails.env.test? ? 1 : 11 )
  end


end