package pkg_test

import (
	"testing"
	"time"

	"github.com/google/uuid"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"github.com/rwadurian/mpc-system/pkg/jwt"
)

func TestJWTService(t *testing.T) {
	jwtService := jwt.NewJWTService(
		"test-secret-key-32-bytes-long!!",
		"test-issuer",
		time.Hour,    // token expiry
		24*time.Hour, // refresh expiry
	)

	t.Run("should generate and validate access token", func(t *testing.T) {
		accountID := "account-123"
		username := "testuser"

		token, err := jwtService.GenerateAccessToken(accountID, username)
		require.NoError(t, err)
		assert.NotEmpty(t, token)

		claims, err := jwtService.ValidateAccessToken(token)
		require.NoError(t, err)
		assert.Equal(t, accountID, claims.Subject)
		assert.Equal(t, username, claims.Username)
		assert.Equal(t, "test-issuer", claims.Issuer)
	})

	t.Run("should generate and validate refresh token", func(t *testing.T) {
		accountID := "account-456"

		token, err := jwtService.GenerateRefreshToken(accountID)
		require.NoError(t, err)
		assert.NotEmpty(t, token)

		claims, err := jwtService.ValidateRefreshToken(token)
		require.NoError(t, err)
		assert.Equal(t, accountID, claims.Subject)
	})

	t.Run("should fail validation with invalid token", func(t *testing.T) {
		_, err := jwtService.ValidateAccessToken("invalid-token")
		assert.Error(t, err)
	})

	t.Run("should fail validation with wrong secret", func(t *testing.T) {
		otherService := jwt.NewJWTService(
			"different-secret-key-32-bytes!",
			"test-issuer",
			time.Hour,
			24*time.Hour,
		)

		token, _ := jwtService.GenerateAccessToken("account", "user")
		_, err := otherService.ValidateAccessToken(token)
		assert.Error(t, err)
	})

	t.Run("should refresh access token", func(t *testing.T) {
		accountID := "account-789"

		refreshToken, _ := jwtService.GenerateRefreshToken(accountID)
		newAccessToken, err := jwtService.RefreshAccessToken(refreshToken)
		require.NoError(t, err)
		assert.NotEmpty(t, newAccessToken)

		claims, err := jwtService.ValidateAccessToken(newAccessToken)
		require.NoError(t, err)
		assert.Equal(t, accountID, claims.Subject)
	})

	t.Run("should fail refresh with invalid token", func(t *testing.T) {
		_, err := jwtService.RefreshAccessToken("invalid-refresh-token")
		assert.Error(t, err)
	})
}

func TestJWTService_JoinToken(t *testing.T) {
	jwtService := jwt.NewJWTService(
		"test-secret-key-32-bytes-long!!",
		"test-issuer",
		time.Hour,
		24*time.Hour,
	)

	t.Run("should generate and validate join token", func(t *testing.T) {
		sessionID := uuid.New()
		partyID := "party-456"

		token, err := jwtService.GenerateJoinToken(sessionID, partyID, 10*time.Minute)
		require.NoError(t, err)
		assert.NotEmpty(t, token)

		claims, err := jwtService.ValidateJoinToken(token, sessionID, partyID)
		require.NoError(t, err)
		assert.Equal(t, sessionID.String(), claims.SessionID)
		assert.Equal(t, partyID, claims.PartyID)
	})

	t.Run("should fail validation with wrong session ID", func(t *testing.T) {
		sessionID := uuid.New()
		wrongSessionID := uuid.New()
		partyID := "party-456"

		token, _ := jwtService.GenerateJoinToken(sessionID, partyID, 10*time.Minute)
		_, err := jwtService.ValidateJoinToken(token, wrongSessionID, partyID)
		assert.Error(t, err)
	})

	t.Run("should fail validation with wrong party ID", func(t *testing.T) {
		sessionID := uuid.New()
		partyID := "party-456"

		token, _ := jwtService.GenerateJoinToken(sessionID, partyID, 10*time.Minute)
		_, err := jwtService.ValidateJoinToken(token, sessionID, "wrong-party")
		assert.Error(t, err)
	})
}

func TestJWTClaims(t *testing.T) {
	t.Run("access token claims should have correct structure", func(t *testing.T) {
		jwtService := jwt.NewJWTService(
			"test-secret-key-32-bytes-long!!",
			"test-issuer",
			time.Hour,
			24*time.Hour,
		)

		token, _ := jwtService.GenerateAccessToken("acc-123", "user123")
		claims, _ := jwtService.ValidateAccessToken(token)

		assert.NotEmpty(t, claims.Subject)
		assert.NotEmpty(t, claims.Username)
		assert.NotEmpty(t, claims.Issuer)
	})
}
