package sys_user_login

import (
	"context"
	"fmt"
	"github.com/gogf/gf/v2/text/gstr"

	"kami/internal/consts"
	"kami/utility/mfa"
	"kami/utility/utils"

	"github.com/gogf/gf/v2/errors/gcode"
	"github.com/gogf/gf/v2/errors/gerror"

	v1 "kami/api/sys_user_login/v1"
	"kami/internal/model"
	"kami/internal/service"
	"kami/utility/token"
)

func (c *ControllerV1) UserLogin(ctx context.Context, req *v1.UserLoginReq) (res *v1.UserLoginRes, err error) {
	var tokenStr string

	// 限流检查：1分钟内最多允许10次登录尝试
	ip := utils.GetIPFromCtx(ctx)
	ipResult := service.Rate().Check(ctx, model.LimiterTypeSysUserLogin, ip)
	if !ipResult.Allowed {
		err = gerror.NewCode(gcode.CodeNotAuthorized, fmt.Sprintf("IP登录过于频繁，请%.0f秒后再试", ipResult.RetryAfter.Seconds()))
		return
	}

	usernameResult := service.Rate().Check(ctx, model.LimiterTypeSysUserLogin, gstr.TrimAll(req.Username))
	if !usernameResult.Allowed {
		err = gerror.NewCode(gcode.CodeNotAuthorized, fmt.Sprintf("该账户登录过于频繁，请%.0f秒后再试", usernameResult.RetryAfter.Seconds()))
		return
	}
	// 判断验证码是否正确
	if !service.Captcha().VerifyString(req.VerifyKey, req.VerifyCode) {
		err = gerror.NewCode(gcode.CodeNotAuthorized, "验证码错误")
		return
	}
	userService := service.SysUser()
	user, err := userService.GetAdminUserByUsernamePassword(ctx,
		&model.UserLoginInput{Username: req.Username, Password: req.Password},
	)
	if err != nil {
		err = gerror.NewCode(gcode.CodeNotAuthorized, "用户不存在或已被禁用")
		return
	}
	if user.OtpSecret != "" && !mfa.ValidCode(req.TotpCode, user.OtpSecret) {
		err = gerror.NewCode(gcode.CodeNotAuthorized, "二步验证错误")
		return
	}
	if user.UserStatus != uint(consts.SysUserStatusNormal) {
		err = gerror.NewCode(gcode.CodeNotAuthorized, "用户已禁用")
		return
	}
	tokenStr, err = token.GenerateUserToken(ctx, user.Username, user.Id)
	if err != nil {
		err = gerror.NewCode(gcode.CodeInternalError, "生成token失败")
		return
	}
	service.SysLoginLog().Invoke(ctx, &model.LoginLogParams{
		Username:  user.Username,
		Ip:        utils.GetIPFromCtx(ctx),
		UserAgent: utils.GetUserAgent(ctx),
		Status:    1,
		Msg:       "登录成功",
	})
	// 获取用户菜单数据
	res = &v1.UserLoginRes{
		Token: tokenStr,
	}
	return
}
