package cube

import (
	"time"

	"github.com/gin-gonic/gin"
	jwt "github.com/golang-jwt/jwt/v4"
	"github.com/marmotedu/errors"
	"github.com/pingcap-inc/pinsights/internal/apiserver/store"
	"github.com/pingcap-inc/pinsights/internal/pkg/code"
	"github.com/pingcap-inc/pinsights/internal/pkg/middleware"
	"github.com/pingcap-inc/pinsights/pkg/log"
	"github.com/pingcap-inc/pinsights/pkg/storage"
	"github.com/pingcap-inc/pinsights/pkg/util/core"
	"github.com/spf13/viper"
)

const CUBE_TOKEN_MID_KEY = "token-"

func GetToken(c *gin.Context) {
	log.L(c).Info("get cube token function called.")

	var authTokenInfo middleware.AuthingTokenInfo
	v, ok := c.Get("AUTHING_TOKEN_INFO")
	authTokenInfo, typeOk := v.(middleware.AuthingTokenInfo)
	if !ok || !typeOk {
		core.WriteResponse(c, errors.WithCode(code.ErrGetAuthingTokenInfo, ""), nil)

		return
	}

	if authTokenInfo.Uid == "" {
		core.WriteResponse(c, errors.WithCode(code.ErrGetAuthingTokenInfo, "%s", "No valid userId from auth middleware"), nil)

		return
	}

	rStore := &storage.RedisCluster{
		KeyPrefix: store.CubeKeyPrefix,
	}

	val, err := rStore.GetKey(CUBE_TOKEN_MID_KEY + authTokenInfo.Uid)
	if err != nil {
		if !errors.As(err, &storage.ErrKeyNotFound) {
			log.Error(errors.Wrap(err, "error when retrieve key in redis").Error())
		}
	}

	cubeToken := val
	if val == "" {
		log.Info("cube token not found in redis. Try to generate a new token.")

		nowUnix := time.Now().Unix()

		token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
			"exp":  authTokenInfo.Exp,
			"sub":  authTokenInfo.Uid,
			"name": authTokenInfo.Uname,
			"iat":  nowUnix,
		})

		tokenString, err := token.SignedString([]byte(viper.GetString("cube.secret")))

		if err != nil {
			core.WriteResponse(c, errors.WithCode(code.ErrGenCubeToken, ""), nil)

			return
		}

		cubeToken = tokenString

		if authTokenInfo.Exp > float64(nowUnix) {
			deltSec := authTokenInfo.Exp - float64(nowUnix)
			deltSecDu := time.Duration(deltSec)*time.Second - time.Second*2
			if deltSecDu > 0 {
				if err := rStore.SetKey(CUBE_TOKEN_MID_KEY+authTokenInfo.Uid, tokenString, deltSecDu); err != nil {
					log.Errorf("Error when store token into redis: %s", err.Error())
				}
			}
		}

	} else {
		log.Info("cube token found in redis. Use it from redis.")
	}

	core.WriteResponse(c, nil, cubeToken)
}
