package component

import (
	"context"
	"errors"
	"fmt"
	"gitee.ltd/lxh/logger/log"
	"github.com/golang-jwt/jwt/v5"
	"strings"
	"time"
	"wireguard-dashboard/client"
	"wireguard-dashboard/constant"
)

const Secret = "IK8MSs76Pb2VJxleTDadf1Wzu3h9QROLv0XtmnCUErYgBG5wAyjk4cioqFZHNpZG"

type JwtClaims struct {
	ID                   string `json:"id"`
	Name                 string `json:"name"`
	jwt.RegisteredClaims `json:"-"`
}

func JWT() JwtClaims {
	return JwtClaims{}
}

// GenerateToken
// @description: 生成token
// @receiver Jwt
// @return token
// @return err
func (j JwtClaims) GenerateToken(userId string) (token string, expireTime *jwt.NumericDate, err error) {
	timeNow := time.Now().Local()
	expireTime = jwt.NewNumericDate(timeNow.Add(7 * time.Hour))
	notBefore := jwt.NewNumericDate(timeNow)
	issuedAt := jwt.NewNumericDate(timeNow)
	claims := JwtClaims{
		ID: userId,
		RegisteredClaims: jwt.RegisteredClaims{
			Subject:   "wireguard-dashboard",
			ExpiresAt: expireTime,
			NotBefore: notBefore,
			IssuedAt:  issuedAt,
		},
	}

	t := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	token, err = t.SignedString([]byte(Secret))
	if err != nil {
		log.Errorf("生成token失败: %v", err.Error())
		return "", nil, errors.New("生成token失败")
	}

	client.Redis.Set(context.Background(), fmt.Sprintf("%s:%s", constant.Token, userId), token, 7*time.Hour)
	return
}

// ParseToken
// @description: 解析token
// @receiver Jwt
// @return Jwt
// @return error
func (JwtClaims) ParseToken(token string) (*JwtClaims, error) {
	tokenStr := strings.Split(token, "Bearer ")[1]

	t, err := jwt.ParseWithClaims(tokenStr, &JwtClaims{}, func(token *jwt.Token) (any, error) {
		return []byte(Secret), nil
	})

	if claims, ok := t.Claims.(*JwtClaims); ok && t.Valid {
		userToken, err := client.Redis.Get(context.Background(), fmt.Sprintf("%s:%s", constant.Token, claims.ID)).Result()
		if err != nil {
			log.Errorf("缓存中用户[%s]的token查找失败: %v", claims.ID, err.Error())
			return nil, errors.New("token不存在")
		}

		if userToken != tokenStr {
			log.Errorf("token不一致")
			return nil, errors.New("token错误")
		}

		return claims, nil
	} else {
		return nil, err
	}
}

// Logout
// @description: 退出登陆
// @receiver JwtClaims
// @param userId
// @return err
func (j JwtClaims) Logout(userId string) (err error) {
	return client.Redis.Del(context.Background(), fmt.Sprintf("%s:%s", constant.Token, userId)).Err()
}