package api

import (
	"encoding/json"
	"errors"

	"github.com/pquerna/otp/totp"

	"github.com/mayswind/ezbookkeeping/pkg/avatars"
	"github.com/mayswind/ezbookkeeping/pkg/core"
	"github.com/mayswind/ezbookkeeping/pkg/duplicatechecker"
	"github.com/mayswind/ezbookkeeping/pkg/errs"
	"github.com/mayswind/ezbookkeeping/pkg/log"
	"github.com/mayswind/ezbookkeeping/pkg/models"
	"github.com/mayswind/ezbookkeeping/pkg/services"
	"github.com/mayswind/ezbookkeeping/pkg/settings"
)

// AuthorizationsApi represents authorization api
type AuthorizationsApi struct {
	ApiUsingConfig
	ApiUsingDuplicateChecker
	ApiWithUserInfo
	users                   *services.UserService
	userAppCloudSettings    *services.UserApplicationCloudSettingsService
	tokens                  *services.TokenService
	twoFactorAuthorizations *services.TwoFactorAuthorizationService
	userExternalAuths       *services.UserExternalAuthService
}

// Initialize a authorization api singleton instance
var (
	Authorizations = &AuthorizationsApi{
		ApiUsingConfig: ApiUsingConfig{
			container: settings.Container,
		},
		ApiUsingDuplicateChecker: ApiUsingDuplicateChecker{
			ApiUsingConfig: ApiUsingConfig{
				container: settings.Container,
			},
			container: duplicatechecker.Container,
		},
		ApiWithUserInfo: ApiWithUserInfo{
			ApiUsingConfig: ApiUsingConfig{
				container: settings.Container,
			},
			ApiUsingAvatarProvider: ApiUsingAvatarProvider{
				container: avatars.Container,
			},
		},
		users:                   services.Users,
		userAppCloudSettings:    services.UserApplicationCloudSettings,
		tokens:                  services.Tokens,
		twoFactorAuthorizations: services.TwoFactorAuthorizations,
		userExternalAuths:       services.UserExternalAuths,
	}
)

// AuthorizeHandler verifies and authorizes current login request
func (a *AuthorizationsApi) AuthorizeHandler(c *core.WebContext) (any, *errs.Error) {
	if !a.CurrentConfig().EnableInternalAuth {
		return nil, errs.ErrCannotLoginByPassword
	}

	var credential models.UserLoginRequest
	err := c.ShouldBindJSON(&credential)

	if err != nil {
		log.Warnf(c, "[authorizations.AuthorizeHandler] parse request failed, because %s", err.Error())
		return nil, errs.ErrLoginNameOrPasswordInvalid
	}

	err = a.CheckFailureCount(c, 0)

	if err != nil {
		log.Warnf(c, "[authorizations.AuthorizeHandler] cannot login for user \"%s\", because %s", credential.LoginName, err.Error())
		return nil, errs.Or(err, errs.ErrFailureCountLimitReached)
	}

	user, uid, err := a.users.GetUserByUsernameOrEmailAndPassword(c, credential.LoginName, credential.Password)

	if errs.IsCustomError(err) {
		failureCheckErr := a.CheckAndIncreaseFailureCount(c, uid)

		if failureCheckErr != nil {
			log.Warnf(c, "[authorizations.AuthorizeHandler] cannot login for user \"%s\", because %s", credential.LoginName, failureCheckErr.Error())
			return nil, errs.Or(failureCheckErr, errs.ErrFailureCountLimitReached)
		}
	}

	if err != nil {
		log.Warnf(c, "[authorizations.AuthorizeHandler] login failed for user \"%s\", because %s", credential.LoginName, err.Error())
		return nil, errs.ErrLoginNameOrPasswordWrong
	}

	if user.Disabled {
		log.Warnf(c, "[authorizations.AuthorizeHandler] login failed for user \"%s\", because user is disabled", credential.LoginName)
		return nil, errs.ErrUserIsDisabled
	}

	if a.CurrentConfig().EnableUserForceVerifyEmail && !user.EmailVerified {
		hasValidEmailVerifyToken, err := a.tokens.ExistsValidTokenByType(c, user.Uid, core.USER_TOKEN_TYPE_EMAIL_VERIFY)

		if err != nil {
			log.Warnf(c, "[authorizations.AuthorizeHandler] failed check whether user \"uid:%d\" has valid verify email token, because %s", user.Uid, err.Error())
			hasValidEmailVerifyToken = false
		}

		log.Warnf(c, "[authorizations.AuthorizeHandler] login failed for user \"%s\", because user has not verified email", credential.LoginName)

		return nil, errs.NewErrorWithContext(errs.ErrEmailIsNotVerified, map[string]any{
			"email":                    user.Email,
			"hasValidEmailVerifyToken": hasValidEmailVerifyToken,
		})
	}

	err = a.users.UpdateUserLastLoginTime(c, user.Uid)

	if err != nil {
		log.Warnf(c, "[authorizations.AuthorizeHandler] failed to update last login time for user \"uid:%d\", because %s", user.Uid, err.Error())
	}

	twoFactorEnable := a.tokens.CurrentConfig().EnableTwoFactor

	if twoFactorEnable {
		twoFactorEnable, err = a.twoFactorAuthorizations.ExistsTwoFactorSetting(c, user.Uid)

		if err != nil {
			log.Errorf(c, "[authorizations.AuthorizeHandler] failed to check two-factor setting for user \"uid:%d\", because %s", user.Uid, err.Error())
			return nil, errs.Or(err, errs.ErrSystemError)
		}
	}

	var token string
	var claims *core.UserTokenClaims

	if twoFactorEnable {
		token, claims, err = a.tokens.CreateRequire2FAToken(c, user)
	} else {
		token, claims, err = a.tokens.CreateToken(c, user)
	}

	if err != nil {
		log.Errorf(c, "[authorizations.AuthorizeHandler] failed to create token for user \"uid:%d\", because %s", user.Uid, err.Error())
		return nil, errs.ErrTokenGenerating
	}

	if !twoFactorEnable {
		c.SetTextualToken(token)
	}

	c.SetTokenClaims(claims)
	c.SetTokenContext("")

	userApplicationCloudSettings, err := a.userAppCloudSettings.GetUserApplicationCloudSettingsByUid(c, user.Uid)
	var applicationCloudSettingSlice *models.ApplicationCloudSettingSlice = nil

	if err != nil {
		log.Warnf(c, "[authorizations.AuthorizeHandler] failed to get latest user application cloud settings for user \"uid:%d\", because %s", user.Uid, err.Error())
	} else if userApplicationCloudSettings != nil && len(userApplicationCloudSettings.Settings) > 0 {
		applicationCloudSettingSlice = &userApplicationCloudSettings.Settings
	}

	log.Infof(c, "[authorizations.AuthorizeHandler] user \"uid:%d\" has logged in, token type is %d, token will be expired at %d", user.Uid, claims.Type, claims.ExpiresAt)

	authResp := a.getAuthResponse(c, token, twoFactorEnable, user, applicationCloudSettingSlice)
	return authResp, nil
}

// TwoFactorAuthorizeHandler verifies and authorizes current 2fa login by passcode
func (a *AuthorizationsApi) TwoFactorAuthorizeHandler(c *core.WebContext) (any, *errs.Error) {
	if !a.CurrentConfig().EnableInternalAuth {
		return nil, errs.ErrCannotLoginByPassword
	}

	var credential models.TwoFactorLoginRequest
	err := c.ShouldBindJSON(&credential)

	if err != nil {
		log.Warnf(c, "[authorizations.TwoFactorAuthorizeHandler] parse request failed, because %s", err.Error())
		return nil, errs.ErrPasscodeInvalid
	}

	uid := c.GetCurrentUid()
	err = a.CheckFailureCount(c, uid)

	if err != nil {
		log.Warnf(c, "[authorizations.TwoFactorAuthorizeHandler] cannot auth for user \"uid:%d\", because %s", uid, err.Error())
		return nil, errs.Or(err, errs.ErrFailureCountLimitReached)
	}

	twoFactorSetting, err := a.twoFactorAuthorizations.GetUserTwoFactorSettingByUid(c, uid)

	if err != nil {
		log.Errorf(c, "[authorizations.TwoFactorAuthorizeHandler] failed to get two-factor setting for user \"uid:%d\", because %s", uid, err.Error())
		return nil, errs.Or(err, errs.ErrSystemError)
	}

	if !totp.Validate(credential.Passcode, twoFactorSetting.Secret) {
		log.Warnf(c, "[authorizations.TwoFactorAuthorizeHandler] passcode is invalid for user \"uid:%d\"", uid)

		err = a.CheckAndIncreaseFailureCount(c, uid)

		if err != nil {
			log.Warnf(c, "[authorizations.TwoFactorAuthorizeHandler] cannot auth for user \"uid:%d\", because %s", uid, err.Error())
			return nil, errs.Or(err, errs.ErrFailureCountLimitReached)
		}

		return nil, errs.ErrPasscodeInvalid
	}

	user, err := a.users.GetUserById(c, uid)

	if err != nil {
		log.Errorf(c, "[authorizations.TwoFactorAuthorizeHandler] failed to get user \"uid:%d\" info, because %s", uid, err.Error())
		return nil, errs.ErrUserNotFound
	}

	if user.Disabled {
		log.Warnf(c, "[authorizations.TwoFactorAuthorizeHandler] user \"uid:%d\" is disabled", user.Uid)
		return nil, errs.ErrUserIsDisabled
	}

	if a.CurrentConfig().EnableUserForceVerifyEmail && !user.EmailVerified {
		log.Warnf(c, "[authorizations.TwoFactorAuthorizeHandler] user \"uid:%d\" has not verified email", user.Uid)
		return nil, errs.ErrEmailIsNotVerified
	}

	oldTokenClaims := c.GetTokenClaims()
	err = a.tokens.DeleteTokenByClaims(c, oldTokenClaims)

	if err != nil {
		log.Warnf(c, "[authorizations.TwoFactorAuthorizeHandler] failed to revoke temporary token \"utid:%s\" for user \"uid:%d\", because %s", oldTokenClaims.UserTokenId, user.Uid, err.Error())
	}

	token, claims, err := a.tokens.CreateToken(c, user)

	if err != nil {
		log.Errorf(c, "[authorizations.TwoFactorAuthorizeHandler] failed to create token for user \"uid:%d\", because %s", user.Uid, err.Error())
		return nil, errs.ErrTokenGenerating
	}

	c.SetTextualToken(token)
	c.SetTokenClaims(claims)
	c.SetTokenContext("")

	userApplicationCloudSettings, err := a.userAppCloudSettings.GetUserApplicationCloudSettingsByUid(c, user.Uid)
	var applicationCloudSettingSlice *models.ApplicationCloudSettingSlice = nil

	if err != nil {
		log.Warnf(c, "[authorizations.TwoFactorAuthorizeHandler] failed to get latest user application cloud settings for user \"uid:%d\", because %s", user.Uid, err.Error())
	} else if userApplicationCloudSettings != nil && len(userApplicationCloudSettings.Settings) > 0 {
		applicationCloudSettingSlice = &userApplicationCloudSettings.Settings
	}

	log.Infof(c, "[authorizations.TwoFactorAuthorizeHandler] user \"uid:%d\" has authorized two-factor via passcode, token will be expired at %d", user.Uid, claims.ExpiresAt)

	authResp := a.getAuthResponse(c, token, false, user, applicationCloudSettingSlice)
	return authResp, nil
}

// TwoFactorAuthorizeByRecoveryCodeHandler verifies and authorizes current 2fa login by recovery code
func (a *AuthorizationsApi) TwoFactorAuthorizeByRecoveryCodeHandler(c *core.WebContext) (any, *errs.Error) {
	if !a.CurrentConfig().EnableInternalAuth {
		return nil, errs.ErrCannotLoginByPassword
	}

	var credential models.TwoFactorRecoveryCodeLoginRequest
	err := c.ShouldBindJSON(&credential)

	if err != nil {
		log.Warnf(c, "[authorizations.TwoFactorAuthorizeByRecoveryCodeHandler] parse request failed, because %s", err.Error())
		return nil, errs.ErrTwoFactorRecoveryCodeInvalid
	}

	uid := c.GetCurrentUid()
	err = a.CheckFailureCount(c, uid)

	if err != nil {
		log.Warnf(c, "[authorizations.TwoFactorAuthorizeByRecoveryCodeHandler] cannot auth for user \"uid:%d\", because %s", uid, err.Error())
		return nil, errs.Or(err, errs.ErrFailureCountLimitReached)
	}

	enableTwoFactor, err := a.twoFactorAuthorizations.ExistsTwoFactorSetting(c, uid)

	if err != nil {
		log.Warnf(c, "[authorizations.TwoFactorAuthorizeByRecoveryCodeHandler] failed to get two-factor setting for user \"uid:%d\", because %s", uid, err.Error())
		return nil, errs.Or(err, errs.ErrSystemError)
	}

	if !enableTwoFactor {
		return nil, errs.ErrTwoFactorIsNotEnabled
	}

	user, err := a.users.GetUserById(c, uid)

	if err != nil {
		log.Errorf(c, "[authorizations.TwoFactorAuthorizeByRecoveryCodeHandler] failed to get user \"uid:%d\" info, because %s", uid, err.Error())
		return nil, errs.ErrUserNotFound
	}

	if user.Disabled {
		log.Warnf(c, "[authorizations.TwoFactorAuthorizeByRecoveryCodeHandler] user \"uid:%d\" is disabled", user.Uid)
		return nil, errs.ErrUserIsDisabled
	}

	if a.CurrentConfig().EnableUserForceVerifyEmail && !user.EmailVerified {
		log.Warnf(c, "[authorizations.TwoFactorAuthorizeByRecoveryCodeHandler] user \"uid:%d\" has not verified email", user.Uid)
		return nil, errs.ErrEmailIsNotVerified
	}

	err = a.twoFactorAuthorizations.GetAndUseUserTwoFactorRecoveryCode(c, uid, credential.RecoveryCode, user.Salt)

	if errs.IsCustomError(err) {
		failureCheckErr := a.CheckAndIncreaseFailureCount(c, uid)

		if failureCheckErr != nil {
			log.Warnf(c, "[authorizations.TwoFactorAuthorizeByRecoveryCodeHandler] cannot auth for user \"uid:%d\", because %s", uid, failureCheckErr.Error())
			return nil, errs.Or(failureCheckErr, errs.ErrFailureCountLimitReached)
		}
	}

	if err != nil {
		log.Warnf(c, "[authorizations.TwoFactorAuthorizeByRecoveryCodeHandler] failed to get two-factor recovery code for user \"uid:%d\", because %s", uid, err.Error())
		return nil, errs.Or(err, errs.ErrTwoFactorRecoveryCodeNotExist)
	}

	oldTokenClaims := c.GetTokenClaims()
	err = a.tokens.DeleteTokenByClaims(c, oldTokenClaims)

	if err != nil {
		log.Warnf(c, "[authorizations.TwoFactorAuthorizeByRecoveryCodeHandler] failed to revoke temporary token \"utid:%s\" for user \"uid:%d\", because %s", oldTokenClaims.UserTokenId, user.Uid, err.Error())
	}

	token, claims, err := a.tokens.CreateToken(c, user)

	if err != nil {
		log.Errorf(c, "[authorizations.TwoFactorAuthorizeByRecoveryCodeHandler] failed to create token for user \"uid:%d\", because %s", user.Uid, err.Error())
		return nil, errs.ErrTokenGenerating
	}

	c.SetTextualToken(token)
	c.SetTokenClaims(claims)
	c.SetTokenContext("")

	userApplicationCloudSettings, err := a.userAppCloudSettings.GetUserApplicationCloudSettingsByUid(c, user.Uid)
	var applicationCloudSettingSlice *models.ApplicationCloudSettingSlice = nil

	if err != nil {
		log.Warnf(c, "[authorizations.TwoFactorAuthorizeByRecoveryCodeHandler] failed to get latest user application cloud settings for user \"uid:%d\", because %s", user.Uid, err.Error())
	} else if userApplicationCloudSettings != nil && len(userApplicationCloudSettings.Settings) > 0 {
		applicationCloudSettingSlice = &userApplicationCloudSettings.Settings
	}

	log.Infof(c, "[authorizations.TwoFactorAuthorizeByRecoveryCodeHandler] user \"uid:%d\" has authorized two-factor via recovery code \"%s\", token will be expired at %d", user.Uid, credential.RecoveryCode, claims.ExpiresAt)

	authResp := a.getAuthResponse(c, token, false, user, applicationCloudSettingSlice)
	return authResp, nil
}

// OAuth2CallbackAuthorizeHandler verifies and authorizes current OAuth 2.0 callback login
func (a *AuthorizationsApi) OAuth2CallbackAuthorizeHandler(c *core.WebContext) (any, *errs.Error) {
	if !a.CurrentConfig().EnableOAuth2Login {
		return nil, errs.ErrOAuth2NotEnabled
	}

	var credential models.OAuth2CallbackLoginRequest
	err := c.ShouldBindJSON(&credential)

	if err != nil {
		log.Warnf(c, "[authorizations.OAuth2CallbackAuthorizeHandler] parse request failed, because %s", err.Error())
		return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
	}

	var tokenContext models.OAuth2CallbackTokenContext
	err = json.Unmarshal([]byte(c.GetTokenContext()), &tokenContext)

	if err != nil {
		log.Warnf(c, "[authorizations.OAuth2CallbackAuthorizeHandler] parse token context failed, because %s", err.Error())
		return nil, errs.ErrOperationFailed
	}

	if !tokenContext.ExternalAuthType.IsValid() {
		log.Warnf(c, "[authorizations.OAuth2CallbackAuthorizeHandler] external auth type \"%s\" is invalid", tokenContext.ExternalAuthType)
		return nil, errs.ErrInvalidOAuth2Provider
	}

	uid := c.GetCurrentUid()
	err = a.CheckFailureCount(c, uid)

	if err != nil {
		log.Warnf(c, "[authorizations.OAuth2CallbackAuthorizeHandler] cannot auth for user \"uid:%d\", because %s", uid, err.Error())
		return nil, errs.Or(err, errs.ErrFailureCountLimitReached)
	}

	user, err := a.users.GetUserById(c, uid)

	if err != nil {
		log.Errorf(c, "[authorizations.OAuth2CallbackAuthorizeHandler] failed to get user \"uid:%d\" info, because %s", uid, err.Error())
		return nil, errs.ErrUserNotFound
	}

	if user.Disabled {
		log.Warnf(c, "[authorizations.OAuth2CallbackAuthorizeHandler] user \"uid:%d\" is disabled", user.Uid)
		return nil, errs.ErrUserIsDisabled
	}

	if a.CurrentConfig().EnableUserForceVerifyEmail && !user.EmailVerified {
		log.Warnf(c, "[authorizations.OAuth2CallbackAuthorizeHandler] user \"uid:%d\" has not verified email", user.Uid)
		return nil, errs.ErrEmailIsNotVerified
	}

	oldTokenClaims := c.GetTokenClaims()

	if oldTokenClaims.Type == core.USER_TOKEN_TYPE_OAUTH2_CALLBACK_REQUIRE_VERIFY {
		if credential.Password == "" {
			return nil, errs.ErrPasswordIsEmpty
		}

		if !a.users.IsPasswordEqualsUserPassword(credential.Password, user) {
			failureCheckErr := a.CheckAndIncreaseFailureCount(c, uid)

			if failureCheckErr != nil {
				log.Warnf(c, "[authorizations.OAuth2CallbackAuthorizeHandler] cannot login for user \"uid:%d\", because %s", user.Uid, failureCheckErr.Error())
				return nil, errs.Or(failureCheckErr, errs.ErrFailureCountLimitReached)
			}

			return nil, errs.ErrUserPasswordWrong
		}

		if a.CurrentConfig().EnableTwoFactor {
			twoFactorSetting, err := a.twoFactorAuthorizations.GetUserTwoFactorSettingByUid(c, uid)

			if err != nil && !errors.Is(err, errs.ErrTwoFactorIsNotEnabled) {
				log.Errorf(c, "[authorizations.OAuth2CallbackAuthorizeHandler] failed to check two-factor setting for user \"uid:%d\", because %s", user.Uid, err.Error())
				return nil, errs.Or(err, errs.ErrSystemError)
			}

			if twoFactorSetting != nil {
				if credential.Passcode == "" {
					return nil, errs.ErrPasscodeEmpty
				}

				if !totp.Validate(credential.Passcode, twoFactorSetting.Secret) {
					log.Warnf(c, "[authorizations.OAuth2CallbackAuthorizeHandler] passcode is invalid for user \"uid:%d\"", uid)

					err = a.CheckAndIncreaseFailureCount(c, uid)

					if err != nil {
						log.Warnf(c, "[authorizations.OAuth2CallbackAuthorizeHandler] cannot auth for user \"uid:%d\", because %s", uid, err.Error())
						return nil, errs.Or(err, errs.ErrFailureCountLimitReached)
					}

					return nil, errs.ErrPasscodeInvalid
				}
			}
		}

		userExternalAuth := &models.UserExternalAuth{
			Uid:              user.Uid,
			ExternalAuthType: tokenContext.ExternalAuthType,
			ExternalUsername: tokenContext.ExternalUsername,
			ExternalEmail:    tokenContext.ExternalEmail,
		}

		err = a.userExternalAuths.CreateUserExternalAuth(c, userExternalAuth)

		if err != nil {
			log.Errorf(c, "[authorizations.OAuth2CallbackAuthorizeHandler] failed to create user external auth for user \"uid:%d\", because %s", user.Uid, err.Error())
			return nil, errs.Or(err, errs.ErrOperationFailed)
		}

		log.Infof(c, "[authorizations.OAuth2CallbackAuthorizeHandler] user external auth has been created for user \"uid:%d\"", user.Uid)
	} else if oldTokenClaims.Type == core.USER_TOKEN_TYPE_OAUTH2_CALLBACK {
		_, err = a.userExternalAuths.GetUserExternalAuthByUid(c, uid, tokenContext.ExternalAuthType)

		if err != nil {
			log.Errorf(c, "[authorizations.OAuth2CallbackAuthorizeHandler] failed to get user external auth for user \"uid:%d\", because %s", uid, err.Error())
			return nil, errs.Or(err, errs.ErrUserExternalAuthNotFound)
		}
	} else {
		return nil, errs.ErrSystemError
	}

	err = a.tokens.DeleteTokenByClaims(c, oldTokenClaims)

	if err != nil {
		log.Warnf(c, "[authorizations.OAuth2CallbackAuthorizeHandler] failed to revoke temporary token \"utid:%s\" for user \"uid:%d\", because %s", oldTokenClaims.UserTokenId, user.Uid, err.Error())
	}

	var token string
	var claims *core.UserTokenClaims

	if credential.Token != "" {
		_, claims, _, err = a.tokens.ParseToken(c, credential.Token)

		if err != nil {
			log.Errorf(c, "[authorizations.OAuth2CallbackAuthorizeHandler] failed to parse token, because %s", err.Error())
			return nil, errs.ErrInvalidToken
		}

		if claims.Uid != user.Uid {
			log.Warnf(c, "[authorizations.OAuth2CallbackAuthorizeHandler] oauth 2.0 user \"uid:%d\" does not match current user \"uid:%d\"", user.Uid, claims.Uid)
			token = ""
			claims = nil
		} else {
			token = credential.Token
		}
	}

	if token == "" {
		token, claims, err = a.tokens.CreateToken(c, user)

		if err != nil {
			log.Errorf(c, "[authorizations.OAuth2CallbackAuthorizeHandler] failed to create token for user \"uid:%d\", because %s", user.Uid, err.Error())
			return nil, errs.ErrTokenGenerating
		}
	}

	c.SetTextualToken(token)
	c.SetTokenClaims(claims)
	c.SetTokenContext("")

	userApplicationCloudSettings, err := a.userAppCloudSettings.GetUserApplicationCloudSettingsByUid(c, user.Uid)
	var applicationCloudSettingSlice *models.ApplicationCloudSettingSlice = nil

	if err != nil {
		log.Warnf(c, "[authorizations.OAuth2CallbackAuthorizeHandler] failed to get latest user application cloud settings for user \"uid:%d\", because %s", user.Uid, err.Error())
	} else if userApplicationCloudSettings != nil && len(userApplicationCloudSettings.Settings) > 0 {
		applicationCloudSettingSlice = &userApplicationCloudSettings.Settings
	}

	log.Infof(c, "[authorizations.OAuth2CallbackAuthorizeHandler] user \"uid:%d\" has logged in, token will be expired at %d", user.Uid, claims.ExpiresAt)

	authResp := a.getAuthResponse(c, token, false, user, applicationCloudSettingSlice)
	return authResp, nil
}

func (a *AuthorizationsApi) getAuthResponse(c *core.WebContext, token string, need2FA bool, user *models.User, applicationCloudSettings *models.ApplicationCloudSettingSlice) *models.AuthResponse {
	return &models.AuthResponse{
		Token:                    token,
		Need2FA:                  need2FA,
		User:                     a.GetUserBasicInfo(user),
		ApplicationCloudSettings: applicationCloudSettings,
		NotificationContent:      a.GetAfterLoginNotificationContent(user.Language, c.GetClientLocale()),
	}
}
