# frozen_string_literal: true

class User::SessionsController < Devise::SessionsController
  def new
    session.delete(:user_sign_in_uid)
    super
  end

  def create
    authenticate!

    if resource.active_for_authentication? && resource.otp_module_enabled?
      if params[:user][:otp_attempt].blank?
        prompt_for_2fa
      else
        attempt_2fa
      end
    else
      continue_sign_in(resource, resource_name)
    end
  end

  private

  def authenticate!
    self.resource = session.key?(:user_sign_in_uid) ? User.find(session.delete(:user_sign_in_uid)) : warden.authenticate!(auth_options)
  end

  def continue_sign_in(resource, resource_name)
    set_flash_message!(:notice, :signed_in)
    sign_in(resource_name, resource)
    yield resource if block_given?
    respond_with resource, location: after_sign_in_path_for(resource)
  end

  def prompt_for_2fa
    session[:user_sign_in_uid] = resource.id
    sign_out(resource)
    warden.lock!
    render "auth/two_factor_authentication"
  end

  def attempt_2fa
    if params[:user][:otp_attempt].length == 8
      try_recovery_code
    elsif resource.authenticate_otp(params[:user][:otp_attempt], drift: APP_CONFIG.fetch(:otp_drift_period, 30).to_i)
      continue_sign_in(resource, resource_name)
    else
      fail_2fa
    end
  end

  def try_recovery_code
    found = TotpRecoveryCode.where(user_id: resource.id, code: params[:user][:otp_attempt].downcase).delete_all
    if found == 1
      flash[:info] = t(".info", count: TotpRecoveryCode.where(user_id: resource.id).count)
      continue_sign_in(resource, resource_name)
    else
      fail_2fa
    end
  end

  def fail_2fa
    sign_out(resource)
    flash[:error] = t(".error")
    redirect_to new_user_session_url
  end
end