package apikey

import (
	"context"
	"fmt"
	"time"

	"github.com/lestrrat-go/jwx/v2/jwa"
	"github.com/lestrrat-go/jwx/v2/jwt"
	"github.com/tidbcloud/account/commons/config"

	"github.com/tidbcloud/tiinf/errs"
)

const (
	uid = "uid"
	upw = "upw"
)

func GenBearerToken(_ context.Context, ak, sk string) (string, error) {
	encAK := GetKeyUtil().EncryptKey(ak)
	encSK := GetKeyUtil().EncryptKey(sk)
	iv := config.Global().OpenAPIGatewayConfig.OpenAPISecretIv

	token, err := jwt.NewBuilder().
		Issuer(config.Iss).
		IssuedAt(time.Now()).
		Claim(uid, encAK).
		Claim(upw, encSK).
		Build()
	if err != nil {
		return "", errs.NewInternalUnknown("gen bearer token error", err)
	}

	tokenB, err := jwt.Sign(token, jwt.WithKey(jwa.HS256, []byte(iv)))
	if err != nil {
		return "", errs.NewInternalUnknown("sign bearer token error", err)
	}

	return string(tokenB), nil
}

func ParseBearerToken(_ context.Context, token string) (string, string, error) {
	iv := config.Global().OpenAPIGatewayConfig.OpenAPISecretIv

	t, err := jwt.Parse([]byte(token), jwt.WithKey(jwa.HS256, []byte(iv)))
	if err != nil {
		return "", "", err
	}

	ak, err := getAndDecryptClaim(t, uid)
	if err != nil {
		return "", "", err
	}
	sk, err := getAndDecryptClaim(t, upw)
	if err != nil {
		return "", "", err
	}
	return ak, sk, nil
}

func getAndDecryptClaim(t jwt.Token, claim string) (string, error) {
	encClaim, exist := t.Get(claim)
	if !exist {
		return "", errs.NewBadRequest(fmt.Sprintf("invalid token: not found %s claim", claim), nil)
	}
	val, err := GetKeyUtil().DecryptKey(encClaim.(string))
	if err != nil {
		return "", errs.NewInternalUnknown(fmt.Sprintf("decrypt %s error", claim), err)
	}
	return val, nil
}
