package token

import (
	"encoding/json"
	"fmt"
	"time"

	"nms_nbi/src/framework/config"
	cachekeyConstants "nms_nbi/src/framework/constants/cachekey"
	tokenConstants "nms_nbi/src/framework/constants/token"
	"nms_nbi/src/framework/logger"
	redisCahe "nms_nbi/src/framework/redis"
	"nms_nbi/src/framework/utils/generate"
	"nms_nbi/src/framework/vo"

	jwt "github.com/golang-jwt/jwt/v5"
)

// Remove 清除登录用户信息UUID
func Remove(tokenStr string) string {
	claims, err := Verify(tokenStr)
	if err != nil {
		logger.Errorf("token verify err %v", err)
		return ""
	}
	// 清除缓存KEY
	uuid := claims[tokenConstants.JWT_UUID].(string)
	tokenKey := cachekeyConstants.LOGIN_TOKEN_KEY + uuid
	hasKey, _ := redisCahe.Has("", tokenKey)
	if hasKey {
		redisCahe.Del("", tokenKey)
	}
	return claims[tokenConstants.JWT_NAME].(string)
}

// Create 令牌生成
func Create(loginUser *vo.LoginUser, ilobArgs ...string) string {
	// 生成用户唯一tokne16位
	loginUser.UUID = generate.Code(16)

	// 设置请求用户登录客户端
	loginUser.IPAddr = ilobArgs[0]
	loginUser.LoginLocation = ilobArgs[1]
	loginUser.OS = ilobArgs[2]
	loginUser.Browser = ilobArgs[3]

	// 设置用户令牌有效期并存入缓存
	Cache(loginUser)

	// 设置登录IP和登录时间
	loginUser.User.LoginIP = loginUser.IPAddr
	loginUser.User.LoginDate = loginUser.LoginTime

	// 令牌算法 HS256 HS384 HS512
	algorithm := config.Get("jwt.algorithm").(string)
	var method *jwt.SigningMethodHMAC
	switch algorithm {
	case "HS512":
		method = jwt.SigningMethodHS512
	case "HS384":
		method = jwt.SigningMethodHS384
	case "HS256":
	default:
		method = jwt.SigningMethodHS256
	}
	// 生成令牌负荷绑定uuid标识
	jwtToken := jwt.NewWithClaims(method, jwt.MapClaims{
		tokenConstants.JWT_UUID: loginUser.UUID,
		tokenConstants.JWT_KEY:  loginUser.UserID,
		tokenConstants.JWT_NAME: loginUser.User.UserName,
		"exp":                   loginUser.ExpireTime,
		"ait":                   loginUser.LoginTime,
	})

	// 生成令牌设置密钥
	secret := config.Get("jwt.secret").(string)
	tokenStr, err := jwtToken.SignedString([]byte(secret))
	if err != nil {
		logger.Infof("jwt sign err : %v", err)
		return ""
	}
	return tokenStr
}

// Cache 缓存登录用户信息
func Cache(loginUser *vo.LoginUser) {
	// 计算配置的有效期
	expTime := config.Get("jwt.expiresIn").(int)
	expTimestamp := time.Duration(expTime) * time.Minute
	iatTimestamp := time.Now().UnixMilli()
	loginUser.LoginTime = iatTimestamp
	loginUser.ExpireTime = iatTimestamp + expTimestamp.Milliseconds()
	// 根据登录标识将loginUser缓存
	tokenKey := cachekeyConstants.LOGIN_TOKEN_KEY + loginUser.UUID
	jsonBytes, err := json.Marshal(loginUser)
	if err != nil {
		return
	}
	redisCahe.SetByExpire("", tokenKey, string(jsonBytes), expTimestamp)
}

// RefreshIn 验证令牌有效期，相差不足xx分钟，自动刷新缓存
func RefreshIn(loginUser *vo.LoginUser) {
	// 相差不足xx分钟，自动刷新缓存
	refreshTime := config.Get("jwt.refreshIn").(int)
	refreshTimestamp := time.Duration(refreshTime) * time.Minute
	// 过期时间
	expireTimestamp := loginUser.ExpireTime
	currentTimestamp := time.Now().UnixMilli()
	if expireTimestamp-currentTimestamp <= refreshTimestamp.Milliseconds() {
		Cache(loginUser)
	}
}

// Verify 校验令牌是否有效
func Verify(tokenString string) (jwt.MapClaims, error) {
	token, err := jwt.Parse(tokenString, func(token *jwt.Token) (any, error) {
		// 判断加密算法是预期的加密算法
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); ok {
			secret := config.Get("jwt.secret").(string)
			return []byte(secret), nil
		}
		return nil, jwt.ErrSignatureInvalid
	})
	if err != nil {
		logger.Errorf("token String Verify : %v", err)
		// 无效身份授权
		return nil, fmt.Errorf("invalid identity authorization")
	}
	// 如果解析负荷成功并通过签名校验
	if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
		return claims, nil
	}
	return nil, fmt.Errorf("token valid error")
}

// LoginUser 缓存的登录用户信息
func LoginUser(claims jwt.MapClaims) vo.LoginUser {
	uuid := claims[tokenConstants.JWT_UUID].(string)
	tokenKey := cachekeyConstants.LOGIN_TOKEN_KEY + uuid
	hasKey, _ := redisCahe.Has("", tokenKey)
	var loginUser vo.LoginUser
	if hasKey {
		loginUserStr, _ := redisCahe.Get("", tokenKey)
		if loginUserStr == "" {
			return loginUser
		}
		err := json.Unmarshal([]byte(loginUserStr), &loginUser)
		if err != nil {
			logger.Errorf("loginuser info json err : %v", err)
			return loginUser
		}
		return loginUser
	}
	return loginUser
}
