package token

import (
	"context"
	"errors"
	"fmt"
	"kami/utility/utils"
	"time"

	"kami/utility/cache"
	"kami/utility/config"

	"github.com/gogf/gf/v2/os/glog"
	"github.com/golang-jwt/jwt/v5"
)

const (
	CacheModelMemory string = "memory"
	CacheModelRedis  string = "redis"
)

type UserToken struct {
	UserID   string
	Username string
	// GrantScope string
	jwt.RegisteredClaims
}

func (t *UserToken) genToken(ctx context.Context) (string, error) {
	token := ""
	tokenCfg, err := config.NewConfig(ctx).GetTokenOptions()
	if err != nil {
		return token, err
	}
	token, err = jwt.NewWithClaims(jwt.SigningMethodHS256, t).SignedString(tokenCfg.EncryptKey)
	return token, err
}

// GenerateUserToken 生成Token
func GenerateUserToken(ctx context.Context, userName, userID string) (string, error) {
	tokenCfg, err := config.NewConfig(ctx).GetTokenOptions()
	if err != nil {
		glog.Errorf(ctx, "获取配置失败%s", err)
		return "", err
	}
	t := UserToken{
		UserID:   userID,
		Username: userName,
		RegisteredClaims: jwt.RegisteredClaims{
			Issuer:    "Auth Server",
			Subject:   userName,
			Audience:  nil,
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Duration(tokenCfg.Timeout) * time.Second)),
			IssuedAt:  jwt.NewNumericDate(time.Now()),
			ID:        utils.GenerateRandomUUID(),
		},
	}
	token, err := t.genToken(ctx)
	if err != nil {
		glog.Errorf(ctx, "生成Token失败%s", err)
		return "", err
	}
	if tokenCfg.CacheModel == CacheModelRedis {
		_ = cache.NewCache().Set(ctx, fmt.Sprintf("Token:%s:%s:%s", tokenCfg.CacheKey, t.UserID, t.ID), token, time.Duration(tokenCfg.Timeout*int64(time.Second)))
	}
	return token, err
}

func GetTokenFromRedis(ctx context.Context, userID string, uuid string) (string, error) {
	tokenCfg, err := config.NewConfig(ctx).GetTokenOptions()
	if err != nil {
		glog.Errorf(ctx, "获取配置失败%s", err)
		return "", err
	}
	token, err := cache.NewCache().Get(ctx, fmt.Sprintf("Token:%s:%s:%s", tokenCfg.CacheKey, userID, uuid))
	if err != nil {
		glog.Errorf(ctx, "获取配置失败%s", err)
		return "", err
	}
	return token.String(), err
}

func ParseUserToken(ctx context.Context, transToken string) (*UserToken, error) {
	transToken = splitTokenFromBearer(transToken)
	userToken := UserToken{}
	tokenCfg, err := config.NewConfig(ctx).GetTokenOptions()
	if err != nil {
		return &userToken, err
	}
	token, err := jwt.ParseWithClaims(transToken, &userToken, func(token *jwt.Token) (i interface{}, err error) {
		return tokenCfg.EncryptKey, nil
	})
	if err != nil {
		return nil, err
	}
	if claims, ok := token.Claims.(*UserToken); ok && token.Valid { // 校验token
		return claims, nil
	}
	return nil, errors.New("invalid token")
}

func RefreshUserToken(ctx context.Context, userToken UserToken) (newToken string, err error) {
	tokenCfg, err := config.NewConfig(ctx).GetTokenOptions()
	if err != nil {
		glog.Errorf(ctx, "获取配置失败%s", err)
		return "", err
	}
	issuerDate, err := userToken.GetIssuedAt()
	if err != nil {
		glog.Error(ctx, "获取签发日期失败")
		return "", errors.New("invalid token")
	}
	if tokenCfg.MaxRefresh == 0 {
		return "", nil
	}
	// 如果符合条件，则刷新token
	if time.Since(issuerDate.Time) > time.Duration(tokenCfg.MaxRefresh) &&
		time.Since(issuerDate.Time) < time.Duration(tokenCfg.Timeout) {
		newToken, err = GenerateUserToken(ctx, userToken.Username, userToken.UserID)
		return newToken, err
	}
	return
}

// DeleteTokenFromRedis 删除Token
func DeleteTokenFromRedis(ctx context.Context, userID string) error {
	tokenCfg, err := config.NewConfig(ctx).GetTokenOptions()
	if err != nil {
		glog.Errorf(ctx, "获取配置失败%s", err)
		return err
	}
	_, _ = cache.NewCache().Remove(ctx, fmt.Sprintf("Token:%s:%s", tokenCfg.CacheKey, userID))
	return err
}
