module DeviseTokenAuth::Concerns::SetUserByToken
  extend ActiveSupport::Concern

  included do
    before_action :set_user_by_token
    after_action :update_auth_header
  end

  # user auth
  def set_user_by_token
    # no default user defined
    return false unless resource_class

    # parse header for values necessary for authentication
    uid        = request.headers['uid']
    @token     = request.headers['access-token']
    @client_id = request.headers['client']

    return false unless @token

    # client_id isn't required, set to 'default' if absent
    @client_id ||= 'default'

    # mitigate timing attacks by finding by uid instead of auth token
    @user = @current_user = uid && resource_class.find_by_uid(uid)

    if @user && @user.valid_token?(@token, @client_id)
      sign_in(:user, @user, store: false, bypass: true)

      # check this now so that the duration of the request itself doesn't eat
      # away the buffer
      @is_batch_request = is_batch_request?(@user, @client_id)
    else
      # zero all values previously set values
      @user = @current_user = @is_batch_request = nil
    end
  end


  def update_auth_header
    # cannot save object if model has invalid params
    return unless @user and @user.valid? and @client_id

    auth_header = {}

    if not DeviseTokenAuth.change_headers_on_each_request
      auth_header = @user.build_auth_header(@token, @client_id)

    # extend expiration of batch buffer to account for the duration of
    # this request
    elsif @is_batch_request
      auth_header = @user.extend_batch_buffer(@token, @client_id)

    # update Authorization response header with new token
    else
      auth_header = @user.create_new_auth_token(@client_id)
    end

    # make sure all values in auth_header are strings!!!
    response.headers.merge!(auth_header)
  end

  def resource_class
    mapping = request.env['devise.mapping'] || Devise.mappings.values.first
    mapping.to
  end

  private

  def is_batch_request?(user, client_id)
    user.tokens[client_id] and
    user.tokens[client_id]['updated_at'] and
    Time.parse(user.tokens[client_id]['updated_at']) > Time.now - DeviseTokenAuth.batch_request_buffer_throttle
  end
end