// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package auth

import (
	"html/template"
	"net/http"
	"strings"

	user_model "code.gitea.io/gitea/models/user"
	"code.gitea.io/gitea/modules/log"
	"code.gitea.io/gitea/modules/optional"
	"code.gitea.io/gitea/modules/setting"
	"code.gitea.io/gitea/modules/timeutil"
	"code.gitea.io/gitea/modules/web/middleware"
	"code.gitea.io/gitea/services/context"
	"code.gitea.io/gitea/services/forms"
	"code.gitea.io/gitea/services/mailer"
	user_service "code.gitea.io/gitea/services/user"
)

func renderPendingAccountRequestSignup(ctx *context.Context, form *forms.RegisterForm, user *user_model.User, email string, message template.HTML, allowResend bool) bool {
	if form != nil {
		middleware.AssignForm(form, ctx.Data)
	}
	if user != nil {
		if !loadAccountRequestValidationMailAttemptData(ctx, user) {
			return false
		}
		if !loadAccountRequestValidationResendData(ctx, user, allowResend) {
			return false
		}
	} else {
		ctx.Data["ShowResendAccountRequestButton"] = false
	}
	ctx.Data["PendingAccountRequestMessage"] = message
	ctx.Data["PendingAccountRequestEmail"] = email
	ctx.HTML(http.StatusOK, tplSignUp)
	return true
}

func loadAccountRequestValidationMailAttemptData(ctx *context.Context, user *user_model.User) bool {
	status, err := user_model.GetAccountRequestValidationMailAttemptStatus(ctx, user)
	if err != nil {
		ctx.ServerError("GetAccountRequestValidationMailAttemptStatus", err)
		return false
	}
	ctx.Data["AccountRequestValidationMailAttemptStatus"] = status

	attemptUnix, err := user_model.GetAccountRequestValidationMailAttemptUnix(ctx, user)
	if err != nil {
		ctx.ServerError("GetAccountRequestValidationMailAttemptUnix", err)
		return false
	}
	ctx.Data["AccountRequestValidationMailAttemptUnix"] = attemptUnix
	return true
}

func loadAccountRequestValidationResendData(ctx *context.Context, user *user_model.User, allowResend bool) bool {
	resendHits, err := user_model.GetAccountRequestValidationResendHits(ctx, user)
	if err != nil {
		ctx.ServerError("GetAccountRequestValidationResendHits", err)
		return false
	}
	ctx.Data["ShowResendAccountRequestButton"] = allowResend && resendHits < user_model.AccountRequestMaxResendHits
	return true
}

func loadActivationMailAttemptData(ctx *context.Context, user *user_model.User) bool {
	status, err := user_model.GetActivationMailAttemptStatus(ctx, user)
	if err != nil {
		ctx.ServerError("GetActivationMailAttemptStatus", err)
		return false
	}
	ctx.Data["AccountRequestValidationMailAttemptStatus"] = status

	attemptUnix, err := user_model.GetActivationMailAttemptUnix(ctx, user)
	if err != nil {
		ctx.ServerError("GetActivationMailAttemptUnix", err)
		return false
	}
	ctx.Data["AccountRequestValidationMailAttemptUnix"] = attemptUnix
	return true
}

func loadActivationResendData(ctx *context.Context, user *user_model.User, allowResend bool) bool {
	resendHits, err := user_model.GetActivationResendHits(ctx, user)
	if err != nil {
		ctx.ServerError("GetActivationResendHits", err)
		return false
	}
	ctx.Data["ShowResendAccountRequestButton"] = allowResend && resendHits < user_model.ActivationMaxResendHits
	return true
}

func loadAdminInviteMailAttemptData(ctx *context.Context, user *user_model.User) bool {
	status, err := user_model.GetAdminInviteMailAttemptStatus(ctx, user)
	if err != nil {
		ctx.ServerError("GetAdminInviteMailAttemptStatus", err)
		return false
	}
	ctx.Data["AccountRequestValidationMailAttemptStatus"] = status

	attemptUnix, err := user_model.GetAdminInviteMailAttemptUnix(ctx, user)
	if err != nil {
		ctx.ServerError("GetAdminInviteMailAttemptUnix", err)
		return false
	}
	ctx.Data["AccountRequestValidationMailAttemptUnix"] = attemptUnix
	return true
}

func loadAdminInviteResendData(ctx *context.Context, user *user_model.User, allowResend bool) bool {
	resendHits, err := user_model.GetAdminInviteResendHits(ctx, user)
	if err != nil {
		ctx.ServerError("GetAdminInviteResendHits", err)
		return false
	}
	ctx.Data["ShowResendAccountRequestButton"] = allowResend && resendHits < user_model.AdminInviteMaxResendHits
	return true
}

func loadAdminInvitePromptData(ctx *context.Context, user *user_model.User, email string, allowResend bool) bool {
	if !loadAdminInviteMailAttemptData(ctx, user) {
		return false
	}
	if !loadAdminInviteResendData(ctx, user, allowResend) {
		return false
	}
	ctx.Data["PendingAccountRequestEmail"] = email
	return true
}

func loadAccountRequestValidationPromptData(ctx *context.Context, user *user_model.User, email string, allowResend bool) bool {
	if !loadAccountRequestValidationMailAttemptData(ctx, user) {
		return false
	}
	if !loadAccountRequestValidationResendData(ctx, user, allowResend) {
		return false
	}
	ctx.Data["PendingAccountRequestEmail"] = email
	return true
}

func loadActivationPromptData(ctx *context.Context, user *user_model.User, email string, allowResend bool) bool {
	if !loadActivationMailAttemptData(ctx, user) {
		return false
	}
	if !loadActivationResendData(ctx, user, allowResend) {
		return false
	}
	ctx.Data["PendingAccountRequestEmail"] = email
	return true
}

func buildRegisterFormFromContext(ctx *context.Context, email string) *forms.RegisterForm {
	form := &forms.RegisterForm{
		UserName: ctx.FormString("user_name"),
		Email:    email,
		Password: ctx.FormString("password"),
		Retype:   ctx.FormString("retype"),
	}
	if form.Email == "" {
		form.Email = ctx.FormString("email")
	}
	return form
}

func renderPendingActivationSignup(ctx *context.Context, form *forms.RegisterForm, user *user_model.User, email string, message template.HTML, allowResend bool) bool {
	if form != nil {
		middleware.AssignForm(form, ctx.Data)
	}
	if user != nil {
		if !loadActivationMailAttemptData(ctx, user) {
			return false
		}
		if !loadActivationResendData(ctx, user, allowResend) {
			return false
		}
	} else {
		ctx.Data["ShowResendAccountRequestButton"] = false
	}
	ctx.Data["PendingAccountRequestMessage"] = message
	ctx.Data["PendingAccountRequestEmail"] = email
	ctx.HTML(http.StatusOK, tplSignUp)
	return true
}

func renderAccountRequestResendResult(ctx *context.Context, user *user_model.User, email, state string, message template.HTML) bool {
	ctx.Data["AccountRequestResendButtonState"] = state

	switch ctx.FormString("source") {
	case "activate_prompt":
		if !loadAccountRequestValidationPromptData(ctx, user, email, true) {
			return false
		}
		renderActivationPromptMessage(ctx, message)
	default:
		if !prepareSignUpPageData(ctx) {
			return false
		}
		form := buildRegisterFormFromContext(ctx, email)
		if !renderPendingAccountRequestSignup(ctx, form, user, email, message, true) {
			return false
		}
	}
	return true
}

func renderActivationResendResult(ctx *context.Context, user *user_model.User, email, state string, message template.HTML) bool {
	ctx.Data["AccountRequestResendButtonState"] = state

	switch ctx.FormString("source") {
	case "activate_prompt":
		if !loadActivationPromptData(ctx, user, email, true) {
			return false
		}
		renderActivationPromptMessage(ctx, message)
	default:
		if !prepareSignUpPageData(ctx) {
			return false
		}
		form := buildRegisterFormFromContext(ctx, email)
		if !renderPendingActivationSignup(ctx, form, user, email, message, true) {
			return false
		}
	}
	return true
}

func renderAdminInvitePrompt(ctx *context.Context, user *user_model.User, email string, message template.HTML, allowResend bool) bool {
	clearSignedUserTemplateData(ctx)
	ctx.Data["Title"] = ctx.Tr("auth.accept_your_invitation")
	if user != nil {
		if !loadAdminInvitePromptData(ctx, user, email, allowResend) {
			return false
		}
	} else {
		ctx.Data["ShowResendAccountRequestButton"] = false
	}
	ctx.Data["ActivationPromptMessage"] = message
	ctx.HTML(http.StatusOK, TplInvitePrompt)
	return true
}

func renderAdminInviteResendResult(ctx *context.Context, user *user_model.User, email, state string, message template.HTML) bool {
	ctx.Data["AccountRequestResendButtonState"] = state
	return renderAdminInvitePrompt(ctx, user, email, message, true)
}

func clearSignedUserTemplateData(ctx *context.Context) {
	ctx.Doer = nil
	ctx.IsSigned = false
	ctx.Data["IsSigned"] = false
	ctx.Data[middleware.ContextDataKeySignedUser] = nil
	ctx.Data["SignedUserID"] = int64(0)
	ctx.Data["IsAdmin"] = false
}

// RenderInactiveAccountPrompt renders the account activation state without exposing a signed-in session.
func RenderInactiveAccountPrompt(ctx *context.Context, user *user_model.User) {
	clearSignedUserTemplateData(ctx)
	ctx.Data["Title"] = ctx.Tr("auth.active_your_account")

	if setting.Service.RegisterManualConfirm {
		status, err := user_model.GetAccountRequestStatus(ctx, user)
		if err != nil {
			ctx.ServerError("GetAccountRequestStatus", err)
			return
		}

		switch status {
		case user_model.AccountRequestStatusPendingEmailValidation:
			expired, err := user_model.IsAccountRequestExpired(ctx, user)
			if err != nil {
				ctx.ServerError("IsAccountRequestExpired", err)
				return
			}
			if expired {
				renderActivationPromptMessage(ctx, ctx.Locale.Tr("auth.account_request_validation_expired"))
				return
			}
			if !loadAccountRequestValidationPromptData(ctx, user, user.Email, true) {
				return
			}
			renderActivationPromptMessage(ctx, ctx.Locale.Tr("auth.account_request_validation_pending_login", user.Email))
			return
		case user_model.AccountRequestStatusPendingAdminReview:
			renderActivationPromptMessage(ctx, ctx.Locale.Tr("auth.account_request_validated_waiting_admin"))
			return
		case user_model.AccountRequestStatusRejected:
			renderActivationPromptMessage(ctx, ctx.Locale.Tr("auth.account_request_rejected_signup"))
			return
		case user_model.AccountRequestStatusBlocked:
			renderActivationPromptMessage(ctx, ctx.Locale.Tr("auth.account_request_blocked", user_model.AccountRequestMaxAttempts))
			return
		}
	}

	if setting.Service.RegisterEmailConfirm {
		isPendingClassicActivation, err := isPendingClassicActivation(ctx, user)
		if err != nil {
			ctx.ServerError("isPendingClassicActivation", err)
			return
		}
		activeCodeLives := timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, ctx.Locale)
		if isPendingClassicActivation {
			if !loadActivationPromptData(ctx, user, user.Email, true) {
				return
			}
		}
		renderActivationPromptMessage(ctx, ctx.Locale.Tr("auth.confirmation_mail_sent_prompt_ex", user.Email, activeCodeLives))
		return
	}

	log.Info("Failed authentication attempt for %s from %s", user.Name, ctx.RemoteAddr())
	ctx.Data["Title"] = ctx.Tr("auth.prohibit_login")
	ctx.HTML(http.StatusOK, "user/auth/prohibit_login")
}

func maybeHandleInactiveLocalSignInPrompt(ctx *context.Context, userNameOrEmail, password string) bool {
	var (
		user *user_model.User
		err  error
	)

	if strings.Contains(userNameOrEmail, "@") {
		user, err = user_model.GetUserByAnyEmail(ctx, userNameOrEmail)
	} else {
		trimmedUserName := strings.TrimSpace(userNameOrEmail)
		if trimmedUserName == "" {
			return false
		}
		user, err = user_model.GetUserByName(ctx, trimmedUserName)
	}
	if err != nil || user.IsActive || user.LoginSource != 0 || !user.ValidatePassword(password) {
		return false
	}
	if redirectPendingAdminInvite(ctx, user) {
		return true
	}
	RenderInactiveAccountPrompt(ctx, user)
	return true
}

func Invitation(ctx *context.Context) {
	email := strings.TrimSpace(ctx.FormString("email"))
	if email == "" {
		ctx.Redirect(setting.AppSubURL + "/user/login")
		return
	}

	user, err := user_model.GetUserByAnyEmail(ctx, email)
	if err != nil {
		ctx.Redirect(setting.AppSubURL + "/user/login")
		return
	}

	isPendingAdminInvite, err := user_model.IsPendingAdminInvite(ctx, user)
	if err != nil {
		ctx.ServerError("IsPendingAdminInvite", err)
		return
	}
	if !isPendingAdminInvite {
		ctx.Redirect(setting.AppSubURL + "/user/login")
		return
	}

	if !renderAdminInvitePrompt(ctx, user, user.Email, ctx.Locale.Tr("auth.invitation_mail_pending_login", user.Email), true) {
		return
	}
}

func isPendingClassicActivation(ctx *context.Context, user *user_model.User) (bool, error) {
	if user == nil || user.IsActive || user.ProhibitLogin || user.LoginSource != 0 || !setting.Service.RegisterEmailConfirm || setting.Service.RegisterManualConfirm {
		return false, nil
	}
	hasValidationState, err := user_model.HasAccountRequestValidationState(ctx, user)
	if err != nil || hasValidationState {
		return false, err
	}
	primaryEmail, err := user_model.GetPrimaryEmailAddressOfUser(ctx, user.ID)
	if err != nil {
		return false, err
	}
	return !primaryEmail.IsActivated, nil
}

func handleExistingActivationOnSignup(ctx *context.Context, form *forms.RegisterForm) bool {
	existing, err := user_model.GetUserByAnyEmail(ctx, form.Email)
	if err != nil {
		if user_model.IsErrUserNotExist(err) || user_model.IsErrEmailInvalid(err) || user_model.IsErrEmailCharIsNotSupported(err) {
			return true
		}
		ctx.ServerError("GetUserByAnyEmail", err)
		return false
	}

	isPendingClassicActivation, err := isPendingClassicActivation(ctx, existing)
	if err != nil {
		ctx.ServerError("isPendingClassicActivation", err)
		return false
	}
	if !isPendingClassicActivation {
		return true
	}

	return renderPendingActivationSignup(ctx, form, existing, existing.Email, ctx.Tr("auth.confirmation_mail_pending_signup", existing.Email), true)
}

func handleExistingAccountRequestOnSignup(ctx *context.Context, form *forms.RegisterForm) bool {
	existing, err := user_model.GetUserByAnyEmail(ctx, form.Email)
	if err != nil {
		if user_model.IsErrUserNotExist(err) || user_model.IsErrEmailInvalid(err) || user_model.IsErrEmailCharIsNotSupported(err) {
			return true
		}
		ctx.ServerError("GetUserByAnyEmail", err)
		return false
	}

	status, err := user_model.GetAccountRequestStatus(ctx, existing)
	if err != nil {
		ctx.ServerError("GetAccountRequestStatus", err)
		return false
	}

	switch status {
	case user_model.AccountRequestStatusPendingEmailValidation:
		expired, err := user_model.IsAccountRequestExpired(ctx, existing)
		if err != nil {
			ctx.ServerError("IsAccountRequestExpired", err)
			return false
		}
		if expired {
			if err := user_service.DeleteUser(ctx, existing, false); err != nil {
				ctx.ServerError("DeleteUser", err)
				return false
			}
			return true
		}
		attempts, blocked, err := user_model.IncrementAccountRequestSignupAttempts(ctx, existing)
		if err != nil {
			ctx.ServerError("IncrementAccountRequestSignupAttempts", err)
			return false
		}
		if blocked {
			return renderPendingAccountRequestSignup(ctx, form, existing, existing.Email, ctx.Tr("auth.account_request_blocked", user_model.AccountRequestMaxAttempts), false)
		}
		_ = attempts
		return renderPendingAccountRequestSignup(ctx, form, existing, existing.Email, ctx.Tr("auth.account_request_validation_pending_signup", existing.Email), true)
	case user_model.AccountRequestStatusPendingAdminReview:
		return renderPendingAccountRequestSignup(ctx, form, existing, existing.Email, ctx.Tr("auth.account_request_admin_review_pending"), false)
	case user_model.AccountRequestStatusRejected:
		return renderPendingAccountRequestSignup(ctx, form, existing, existing.Email, ctx.Tr("auth.account_request_rejected_signup"), false)
	case user_model.AccountRequestStatusBlocked:
		return renderPendingAccountRequestSignup(ctx, form, existing, existing.Email, ctx.Tr("auth.account_request_blocked", user_model.AccountRequestMaxAttempts), false)
	default:
		return true
	}
}

func handleAccountRequestValidation(ctx *context.Context, user *user_model.User) {
	status, err := user_model.GetAccountRequestStatus(ctx, user)
	if err != nil {
		ctx.ServerError("GetAccountRequestStatus", err)
		return
	}

	switch status {
	case user_model.AccountRequestStatusPendingEmailValidation:
		expired, err := user_model.IsAccountRequestExpired(ctx, user)
		if err != nil {
			ctx.ServerError("IsAccountRequestExpired", err)
			return
		}
		if expired {
			renderActivationPromptMessage(ctx, ctx.Locale.Tr("auth.account_request_validation_expired"))
			return
		}
		hasUsers, err := user_model.HasUsers(ctx)
		if err != nil {
			ctx.ServerError("HasUsers", err)
			return
		}
		if hasUsers.HasOnlyOneUser {
			if err := user_model.ApproveAccountRequest(ctx, user, 0); err != nil {
				ctx.ServerError("ApproveAccountRequest", err)
				return
			}
			if err := user_service.UpdateUser(ctx, user, &user_service.UpdateOptions{
				IsAdmin:      user_service.UpdateOptionFieldFromValue(true),
				IsActive:     optional.Some(true),
				SetLastLogin: true,
			}); err != nil {
				ctx.ServerError("UpdateUser", err)
				return
			}
			renderActivationPromptMessage(ctx, ctx.Locale.Tr("auth.account_activated"))
			return
		}
		if err := user_model.MarkAccountRequestPendingAdminReview(ctx, user); err != nil {
			ctx.ServerError("MarkAccountRequestPendingAdminReview", err)
			return
		}
		if err := mailer.SendNewAccountRequestMail(ctx, user); err != nil {
			log.Error("SendNewAccountRequestMail: %v", err)
		}
		renderActivationPromptMessage(ctx, ctx.Locale.Tr("auth.account_request_validated_waiting_admin"))
	case user_model.AccountRequestStatusPendingAdminReview:
		renderActivationPromptMessage(ctx, ctx.Locale.Tr("auth.account_request_admin_review_pending"))
	case user_model.AccountRequestStatusRejected:
		renderActivationPromptMessage(ctx, ctx.Locale.Tr("auth.account_request_rejected_signup"))
	case user_model.AccountRequestStatusBlocked:
		renderActivationPromptMessage(ctx, ctx.Locale.Tr("auth.account_request_blocked", user_model.AccountRequestMaxAttempts))
	default:
		renderActivationPromptMessage(ctx, ctx.Locale.Tr("auth.invalid_code"))
	}
}

func ResendAccountRequestValidationPost(ctx *context.Context) {
	if setting.Service.DisableRegistration || setting.Service.AllowOnlyExternalRegistration {
		ctx.HTTPError(http.StatusForbidden)
		return
	}

	email := strings.TrimSpace(ctx.FormString("email"))
	user, err := user_model.GetUserByAnyEmail(ctx, email)
	if err != nil {
		if user_model.IsErrUserNotExist(err) || user_model.IsErrEmailInvalid(err) || user_model.IsErrEmailCharIsNotSupported(err) {
			ctx.Flash.Error(ctx.Tr("auth.account_request_resend_not_available"))
			ctx.Redirect(setting.AppSubURL + "/user/sign_up")
			return
		}
		ctx.ServerError("GetUserByAnyEmail", err)
		return
	}

	isPendingClassicActivation, err := isPendingClassicActivation(ctx, user)
	if err != nil {
		ctx.ServerError("isPendingClassicActivation", err)
		return
	}
	if isPendingClassicActivation {
		resendHits, err := user_model.GetActivationResendHits(ctx, user)
		if err != nil {
			ctx.ServerError("GetActivationResendHits", err)
			return
		}
		if resendHits >= user_model.ActivationMaxResendHits {
			ctx.Flash.Error(ctx.Tr("auth.confirmation_mail_resend_not_available"))
			ctx.Redirect(setting.AppSubURL + "/user/sign_up")
			return
		}

		if err := mailer.SendActivateAccountMail(ctx.Locale, user); err != nil {
			if recordErr := user_model.RecordActivationMailAttempt(ctx, user.ID, false); recordErr != nil {
				log.Error("RecordActivationMailAttempt: %v", recordErr)
			}
			log.Error("SendActivateAccountMail: %v", err)
			renderActivationResendResult(ctx, user, email, "failed", ctx.Tr("auth.confirmation_mail_resend_failed"))
			return
		}
		if err := user_model.RecordActivationMailAttempt(ctx, user.ID, true); err != nil {
			ctx.ServerError("RecordActivationMailAttempt", err)
			return
		}
		if _, err := user_model.IncrementActivationResendHits(ctx, user); err != nil {
			ctx.ServerError("IncrementActivationResendHits", err)
			return
		}
		activeCodeLives := timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, ctx.Locale)
		renderActivationResendResult(ctx, user, email, "success", ctx.Tr("auth.confirmation_mail_sent_prompt_ex", email, activeCodeLives))
		return
	}

	status, err := user_model.GetAccountRequestStatus(ctx, user)
	if err != nil {
		ctx.ServerError("GetAccountRequestStatus", err)
		return
	}
	if status != user_model.AccountRequestStatusPendingEmailValidation {
		ctx.Flash.Error(ctx.Tr("auth.account_request_resend_not_available"))
		ctx.Redirect(setting.AppSubURL + "/user/sign_up")
		return
	}
	resendHits, err := user_model.GetAccountRequestValidationResendHits(ctx, user)
	if err != nil {
		ctx.ServerError("GetAccountRequestValidationResendHits", err)
		return
	}
	if resendHits >= user_model.AccountRequestMaxResendHits {
		ctx.Flash.Error(ctx.Tr("auth.account_request_resend_not_available"))
		ctx.Redirect(setting.AppSubURL + "/user/sign_up")
		return
	}

	if err := user_model.RefreshAccountRequestValidationWindow(ctx, user); err != nil {
		ctx.ServerError("RefreshAccountRequestValidationWindow", err)
		return
	}
	validationCode := user_model.GenerateAccountRequestValidationCode(user)
	if err := mailer.SendAccountRequestValidationMail(ctx.Locale, user, validationCode); err != nil {
		if recordErr := user_model.RecordAccountRequestValidationMailAttempt(ctx, user.ID, false); recordErr != nil {
			log.Error("RecordAccountRequestValidationMailAttempt: %v", recordErr)
		}
		log.Error("SendAccountRequestValidationMail: %v", err)
		renderAccountRequestResendResult(ctx, user, email, "failed", ctx.Tr("auth.account_request_validation_resend_failed"))
		return
	}
	if err := user_model.SetAccountRequestValidationCode(ctx, user.ID, validationCode); err != nil {
		ctx.ServerError("SetAccountRequestValidationCode", err)
		return
	}
	if err := user_model.RecordAccountRequestValidationMailAttempt(ctx, user.ID, true); err != nil {
		ctx.ServerError("RecordAccountRequestValidationMailAttempt", err)
		return
	}
	if _, err := user_model.IncrementAccountRequestValidationResendHits(ctx, user); err != nil {
		ctx.ServerError("IncrementAccountRequestValidationResendHits", err)
		return
	}

	renderAccountRequestResendResult(ctx, user, email, "success", ctx.Tr("auth.account_request_validation_resent"))
}

func ResendAdminInvitePost(ctx *context.Context) {
	email := strings.TrimSpace(ctx.FormString("email"))
	user, err := user_model.GetUserByAnyEmail(ctx, email)
	if err != nil {
		ctx.Flash.Error(ctx.Tr("auth.invitation_mail_resend_not_available"))
		ctx.Redirect(setting.AppSubURL + "/user/login")
		return
	}

	isPendingAdminInvite, err := user_model.IsPendingAdminInvite(ctx, user)
	if err != nil {
		ctx.ServerError("IsPendingAdminInvite", err)
		return
	}
	if !isPendingAdminInvite {
		ctx.Flash.Error(ctx.Tr("auth.invitation_mail_resend_not_available"))
		ctx.Redirect(setting.AppSubURL + "/user/login")
		return
	}

	resendHits, err := user_model.GetAdminInviteResendHits(ctx, user)
	if err != nil {
		ctx.ServerError("GetAdminInviteResendHits", err)
		return
	}
	if resendHits >= user_model.AdminInviteMaxResendHits {
		renderAdminInviteResendResult(ctx, user, email, "failed", ctx.Tr("auth.invitation_mail_resend_not_available"))
		return
	}

	adminName, err := user_model.GetUserSetting(ctx, user.ID, user_model.SettingsKeyAdminInviteCreatedByName)
	if err != nil && !user_model.IsErrUserSettingIsNotExist(err) {
		ctx.ServerError("GetUserSetting", err)
		return
	}
	if adminName == "" {
		adminName = setting.AppName
	}

	if err := mailer.SendAdminInviteMailSync(adminName, user); err != nil {
		if recordErr := user_model.RecordAdminInviteMailAttempt(ctx, user.ID, false); recordErr != nil {
			log.Error("RecordAdminInviteMailAttempt: %v", recordErr)
		}
		log.Error("SendAdminInviteMailSync: %v", err)
		renderAdminInviteResendResult(ctx, user, email, "failed", ctx.Tr("auth.invitation_mail_resend_failed"))
		return
	}
	if err := user_model.RecordAdminInviteMailAttempt(ctx, user.ID, true); err != nil {
		ctx.ServerError("RecordAdminInviteMailAttempt", err)
		return
	}
	if _, err := user_model.IncrementAdminInviteResendHits(ctx, user); err != nil {
		ctx.ServerError("IncrementAdminInviteResendHits", err)
		return
	}

	inviteCodeLives := timeutil.MinutesToFriendly(user_model.AdminInviteCodeLives, ctx.Locale)
	renderAdminInviteResendResult(ctx, user, email, "success", ctx.Tr("auth.invitation_mail_sent_prompt_ex", email, inviteCodeLives))
}
