package dbhubsdk

import (
	"testing"
	"time"

	"github.com/dgrijalva/jwt-go"
	"github.com/stretchr/testify/require"
	"github.com/tidbcloud/dataservices/idl/pbgen/proto/dataapi"
)

func TestParseJwtToken(t *testing.T) {
	expiresAt := time.Now().Add(time.Hour).Unix()
	token := mustSignTestJWT(t, expiresAt)

	claims, err := ParseJwtToken(token)
	require.NoError(t, err)
	require.NotNil(t, claims)
	require.Equal(t, expiresAt, claims.ExpiresAt)

	resp := &JwtResp{
		Jwt:    token,
		DbUser: "root",
	}
	resp.ParseJwtExpiresAt()
	require.Equal(t, expiresAt, resp.ExpiresAt)
}

func TestJwtClientCache(t *testing.T) {
	oldTicker := DefDeleteJwtExpiredCacheTicker
	DefDeleteJwtExpiredCacheTicker = 100 * time.Millisecond
	defer func() {
		DefDeleteJwtExpiredCacheTicker = oldTicker
	}()

	cli := NewJwtClient()
	require.Nil(t, cli.GetJwtFromCache(dataapi.CrtType_AUTH0, 1, "abc"))

	stableJwt := &JwtResp{
		Jwt:       mustSignTestJWT(t, time.Now().Add(time.Hour).Unix()),
		DbUser:    "u1",
		ExpiresAt: time.Now().Add(time.Hour).Unix(),
	}
	nearExpiryJwt := &JwtResp{
		Jwt:       mustSignTestJWT(t, time.Now().Add(2*time.Second).Unix()),
		DbUser:    "u2",
		ExpiresAt: time.Now().Add(2 * time.Second).Unix(),
	}

	cli.StoreJwtIntoCache(dataapi.CrtType_AUTH0, 1, "abc", stableJwt)
	cli.StoreJwtIntoCache(dataapi.CrtType_APIKEY, 2, "12345", nearExpiryJwt)

	require.Equal(t, stableJwt, cli.GetJwtFromCache(dataapi.CrtType_AUTH0, 1, "abc"))
	require.Nil(t, cli.GetJwtFromCache(dataapi.CrtType_APIKEY, 2, "12345"))

	time.Sleep(3 * time.Second)

	require.Equal(t, stableJwt, cli.GetJwtFromCache(dataapi.CrtType_AUTH0, 1, "abc"))
	require.Nil(t, cli.GetJwtFromCache(dataapi.CrtType_APIKEY, 2, "12345"))

	size := 0
	cli.cache.Range(func(key, value any) bool {
		size++
		return true
	})
	require.Equal(t, 1, size)
}

func mustSignTestJWT(t *testing.T, expiresAt int64) string {
	t.Helper()

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, &jwt.StandardClaims{
		ExpiresAt: expiresAt,
	})
	signed, err := token.SignedString([]byte("random"))
	require.NoError(t, err)
	return signed
}
