package chunking

import (
	"fmt"
	"runtime"
	"strings"
	"sync"
	"testing"
	"time"

	"github.com/gorilla/sessions"
)

// TestTokenValidatorJWT tests JWT validation using TokenValidator
func TestTokenValidatorJWT(t *testing.T) {
	validator := NewTokenValidator()

	// Test valid JWT format (using base64url encoded parts that are long enough)
	validJWT := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" // trufflehog:ignore
	err := validator.ValidateJWTFormat(validJWT, "test")
	if err != nil {
		t.Errorf("Expected valid JWT to pass, got error: %v", err)
	}

	// Test invalid JWT format - too few parts
	invalidJWT := "header.payload"
	err = validator.ValidateJWTFormat(invalidJWT, "test")
	if err == nil {
		t.Error("Expected invalid JWT to fail validation")
	}

	// Test invalid JWT format - too many parts
	invalidJWT2 := "header.payload.signature.extra"
	err = validator.ValidateJWTFormat(invalidJWT2, "test")
	if err == nil {
		t.Error("Expected invalid JWT with extra parts to fail validation")
	}

	// Test empty JWT
	err = validator.ValidateJWTFormat("", "test")
	if err != nil {
		t.Error("Expected empty JWT to pass validation (empty is allowed)")
	}
}

// TestTokenValidatorOpaqueToken tests opaque token validation using TokenValidator
func TestTokenValidatorOpaqueToken(t *testing.T) {
	validator := NewTokenValidator()
	config := AccessTokenConfig

	// Test valid opaque token with more entropy
	validOpaque := "z8Bx5mP9qK3nL4wR7tY2uI0oE6cV1aS"
	err := validator.ValidateTokenContent(validOpaque, config)
	if err != nil {
		t.Errorf("Expected valid opaque token to pass, got error: %v", err)
	}

	// Test too short opaque token
	shortOpaque := "short"
	err = validator.ValidateTokenContent(shortOpaque, config)
	if err == nil {
		t.Error("Expected short opaque token to fail validation")
	}

	// Test empty opaque token
	err = validator.ValidateTokenContent("", config)
	if err != nil {
		t.Error("Expected empty opaque token to pass validation (empty is allowed)")
	}
}

// TestTokenValidatorTokenSize tests token size validation using TokenValidator
func TestTokenValidatorTokenSize(t *testing.T) {
	validator := NewTokenValidator()

	// Test normal token size
	normalToken := strings.Repeat("a", 1000)
	err := validator.ValidateTokenSize(normalToken, AccessTokenConfig)
	if err != nil {
		t.Errorf("Expected normal token to pass size validation, got error: %v", err)
	}

	// Test oversized token
	oversizedToken := strings.Repeat("a", AccessTokenConfig.MaxLength+1)
	err = validator.ValidateTokenSize(oversizedToken, AccessTokenConfig)
	if err == nil {
		t.Error("Expected oversized token to fail validation")
	}

	// Test undersized token
	undersizedToken := "ab"
	err = validator.ValidateTokenSize(undersizedToken, AccessTokenConfig)
	if err == nil {
		t.Error("Expected undersized token to fail validation")
	}
}

// TestTokenValidatorTokenContent tests token content validation using TokenValidator
func TestTokenValidatorTokenContent(t *testing.T) {
	validator := NewTokenValidator()

	// Test normal token content with good entropy
	normalToken := "A9zZ8yX7wV6uT5sR4qP3oN2mL1kJ0iH"
	err := validator.ValidateTokenContent(normalToken, AccessTokenConfig)
	if err != nil {
		t.Errorf("Expected normal token to pass content validation, got error: %v", err)
	}

	// Test token with null bytes
	nullByteToken := "token_with\x00null_byte"
	err = validator.ValidateTokenContent(nullByteToken, AccessTokenConfig)
	if err == nil {
		t.Error("Expected token with null bytes to fail validation")
	}

	// Test token with control characters
	controlCharToken := "token_with\x01control"
	err = validator.ValidateTokenContent(controlCharToken, AccessTokenConfig)
	if err == nil {
		t.Error("Expected token with control characters to fail validation")
	}
}

// TestChunkManagerSingleTokenValidation tests single token validation path
func TestChunkManagerSingleTokenValidation(t *testing.T) {
	cm := NewChunkManager(nil)

	// Create a valid opaque token with good entropy
	validToken := "oP8qW7rE6tY5uI4oP3aS2dF1gH9jK0lZ3xC6vB5nM4"

	// Test valid token processing
	result := cm.processSingleToken(validToken, false, AccessTokenConfig)
	if result.Error != nil {
		t.Errorf("Expected valid token to process successfully, got error: %v", result.Error)
	}
	if result.Token != validToken {
		t.Error("Expected token to be returned unchanged")
	}

	// Test invalid token processing
	invalidToken := "invalid.token"
	result = cm.processSingleToken(invalidToken, false, IDTokenConfig) // ID tokens require JWT format
	if result.Error == nil {
		t.Error("Expected invalid token to fail processing")
	}
}

// TestTokenConfigValidation tests different token configurations
func TestTokenConfigValidation(t *testing.T) {
	tests := []struct {
		name   string
		config TokenConfig
	}{
		{
			name:   "AccessTokenConfig",
			config: AccessTokenConfig,
		},
		{
			name:   "RefreshTokenConfig",
			config: RefreshTokenConfig,
		},
		{
			name:   "IDTokenConfig",
			config: IDTokenConfig,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Verify config has expected fields
			if tt.config.Type == "" {
				t.Error("Expected config to have Type set")
			}
			if tt.config.MaxLength <= 0 {
				t.Error("Expected config to have positive MaxLength")
			}
			if tt.config.MinLength <= 0 {
				t.Error("Expected config to have positive MinLength")
			}
		})
	}
}

// TestSessionMapBounds_HardLimitEnforcement tests that the session map enforces hard limits
// and prevents unbounded memory growth
func TestSessionMapBounds_HardLimitEnforcement(t *testing.T) {
	tests := []struct {
		name           string
		description    string
		maxSessions    int
		sessionCount   int
		expectEviction bool
	}{
		{
			name:           "within_limit",
			maxSessions:    100,
			sessionCount:   50,
			expectEviction: false,
			description:    "Sessions within limit should not trigger eviction",
		},
		{
			name:           "at_limit",
			maxSessions:    100,
			sessionCount:   100,
			expectEviction: false,
			description:    "Sessions at exact limit should not trigger eviction",
		},
		{
			name:           "exceeds_limit",
			maxSessions:    100,
			sessionCount:   150,
			expectEviction: true,
			description:    "Sessions exceeding limit should trigger eviction",
		},
		{
			name:           "small_limit",
			maxSessions:    10,
			sessionCount:   20,
			expectEviction: true,
			description:    "Small limit should be strictly enforced",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Create chunk manager with custom limits
			cm := NewChunkManager(nil)
			cm.maxSessions = tt.maxSessions

			// Record initial memory
			runtime.GC()
			var m1 runtime.MemStats
			runtime.ReadMemStats(&m1)

			// Create sessions by storing them in the session map
			for i := 0; i < tt.sessionCount; i++ {
				sessionKey := generateSessionKey(i)

				// Create a mock session entry
				cm.mutex.Lock()
				cm.sessionMap[sessionKey] = &SessionEntry{
					Session:   &sessions.Session{Values: make(map[interface{}]interface{})},
					ExpiresAt: time.Now().Add(24 * time.Hour),
					LastUsed:  time.Now(),
				}
				cm.mutex.Unlock()

				// Trigger cleanup every 10 sessions to test enforcement
				if i%10 == 9 {
					cm.CleanupExpiredSessions()
				}
			}

			// Force final cleanup to enforce limits
			cm.CleanupExpiredSessions()

			// Check final session count
			cm.mutex.RLock()
			finalSessionCount := len(cm.sessionMap)
			cm.mutex.RUnlock()

			// Verify hard limit enforcement
			if finalSessionCount > tt.maxSessions {
				t.Errorf("Hard limit not enforced: %s\nMax sessions: %d\nFinal session count: %d\nExpected eviction: %v",
					tt.description, tt.maxSessions, finalSessionCount, tt.expectEviction)
			}

			// Verify eviction occurred if expected
			if tt.expectEviction && finalSessionCount >= tt.sessionCount {
				t.Errorf("Expected eviction did not occur: %s\nCreated sessions: %d\nFinal sessions: %d",
					tt.description, tt.sessionCount, finalSessionCount)
			}

			// Record final memory
			runtime.GC()
			var m2 runtime.MemStats
			runtime.ReadMemStats(&m2)
			memoryGrowth := m2.Alloc - m1.Alloc

			t.Logf("Test %s: Created %d sessions, Final count: %d, Memory growth: %d bytes",
				tt.name, tt.sessionCount, finalSessionCount, memoryGrowth)

			// Verify memory growth is bounded
			maxExpectedMemoryPerSession := int64(1024) // 1KB per session
			maxExpectedMemory := int64(tt.maxSessions) * maxExpectedMemoryPerSession
			if int64(memoryGrowth) > maxExpectedMemory*2 { // Allow 2x tolerance
				t.Errorf("Memory growth exceeds expected bounds: %d bytes (max expected: %d)",
					memoryGrowth, maxExpectedMemory)
			}
		})
	}
}

// TestSessionMapBounds_EmergencyCleanup tests that emergency cleanup triggers when approaching limits
func TestSessionMapBounds_EmergencyCleanup(t *testing.T) {
	cm := NewChunkManager(nil)
	cm.maxSessions = 50

	// Force lastCleanup to be old so cleanup will run
	cm.lastCleanup = time.Now().Add(-2 * time.Hour)

	// Fill sessions to near capacity
	nearCapacity := cm.maxSessions - 5
	for i := 0; i < nearCapacity; i++ {
		sessionKey := generateSessionKey(i)
		cm.mutex.Lock()
		cm.sessionMap[sessionKey] = &SessionEntry{
			Session:   &sessions.Session{Values: make(map[interface{}]interface{})},
			ExpiresAt: time.Now().Add(24 * time.Hour),
			LastUsed:  time.Now().Add(time.Duration(-i) * time.Hour), // Vary ages for LRU
		}
		cm.mutex.Unlock()
	}

	// Add some expired sessions that should be cleaned up
	expiredCount := 10
	for i := 0; i < expiredCount; i++ {
		sessionKey := generateExpiredSessionKey(i)
		cm.mutex.Lock()
		cm.sessionMap[sessionKey] = &SessionEntry{
			Session:   &sessions.Session{Values: make(map[interface{}]interface{})},
			ExpiresAt: time.Now().Add(-24 * time.Hour), // Expired
			LastUsed:  time.Now().Add(-48 * time.Hour),
		}
		cm.mutex.Unlock()
	}

	// Record state before emergency cleanup
	cm.mutex.RLock()
	beforeCleanup := len(cm.sessionMap)
	cm.mutex.RUnlock()

	// Trigger emergency cleanup
	cm.CleanupExpiredSessions()

	// Check that expired sessions were removed
	cm.mutex.RLock()
	afterCleanup := len(cm.sessionMap)
	cm.mutex.RUnlock()

	cleanedUp := beforeCleanup - afterCleanup
	if cleanedUp < expiredCount {
		t.Errorf("Emergency cleanup insufficient: cleaned %d sessions, expected at least %d",
			cleanedUp, expiredCount)
	}

	// Verify we're still within limits
	if afterCleanup > cm.maxSessions {
		t.Errorf("Emergency cleanup failed to enforce limits: %d sessions > %d max",
			afterCleanup, cm.maxSessions)
	}

	t.Logf("Emergency cleanup: Before: %d, After: %d, Cleaned: %d",
		beforeCleanup, afterCleanup, cleanedUp)
}

// TestSessionMapBounds_EvictionUnderHighLoad tests session eviction under high concurrent load
func TestSessionMapBounds_EvictionUnderHighLoad(t *testing.T) {
	cm := NewChunkManager(nil)
	cm.maxSessions = 100

	// Record initial memory
	runtime.GC()
	var m1 runtime.MemStats
	runtime.ReadMemStats(&m1)

	const numGoroutines = 10
	const sessionsPerGoroutine = 50
	var wg sync.WaitGroup

	// Create sessions concurrently to simulate high load
	for g := 0; g < numGoroutines; g++ {
		wg.Add(1)
		go func(goroutineID int) {
			defer wg.Done()
			for i := 0; i < sessionsPerGoroutine; i++ {
				sessionKey := generateConcurrentSessionKey(goroutineID, i)

				cm.mutex.Lock()
				cm.sessionMap[sessionKey] = &SessionEntry{
					Session:   &sessions.Session{Values: make(map[interface{}]interface{})},
					ExpiresAt: time.Now().Add(24 * time.Hour),
					LastUsed:  time.Now(),
				}

				// Randomly trigger cleanup to test concurrent access
				if i%10 == goroutineID%10 {
					cm.mutex.Unlock()
					cm.CleanupExpiredSessions()
				} else {
					cm.mutex.Unlock()
				}

				// Small delay to increase concurrency contention
				time.Sleep(time.Microsecond)
			}
		}(g)
	}

	wg.Wait()

	// Final cleanup
	cm.CleanupExpiredSessions()

	// Verify limits are still enforced
	cm.mutex.RLock()
	finalCount := len(cm.sessionMap)
	cm.mutex.RUnlock()

	if finalCount > cm.maxSessions {
		t.Errorf("High load caused limit breach: %d sessions > %d max", finalCount, cm.maxSessions)
	}

	// Check memory usage
	runtime.GC()
	var m2 runtime.MemStats
	runtime.ReadMemStats(&m2)
	memoryGrowth := m2.Alloc - m1.Alloc

	t.Logf("High load test: Created %d total sessions, Final count: %d, Memory growth: %d bytes",
		numGoroutines*sessionsPerGoroutine, finalCount, memoryGrowth)

	// Verify memory is bounded
	maxExpectedMemory := int64(cm.maxSessions * 2048) // 2KB per session
	if int64(memoryGrowth) > maxExpectedMemory {
		t.Errorf("Memory growth under high load: %d bytes > %d expected max",
			memoryGrowth, maxExpectedMemory)
	}
}

// TestSessionMapBounds_NoMemoryGrowthBeyondLimits tests that memory doesn't grow beyond configured limits
func TestSessionMapBounds_NoMemoryGrowthBeyondLimits(t *testing.T) {
	const maxSessions = 200
	const testIterations = 1000 // Create way more sessions than limit

	cm := NewChunkManager(nil)
	cm.maxSessions = maxSessions

	// Record baseline memory
	runtime.GC()
	runtime.GC()
	var baseline runtime.MemStats
	runtime.ReadMemStats(&baseline)

	// Create sessions in waves, exceeding limits
	for wave := 0; wave < 5; wave++ {
		// Create burst of sessions
		for i := 0; i < testIterations/5; i++ {
			sessionKey := generateWaveSessionKey(wave, i)

			cm.mutex.Lock()
			cm.sessionMap[sessionKey] = &SessionEntry{
				Session:   &sessions.Session{Values: make(map[interface{}]interface{})},
				ExpiresAt: time.Now().Add(24 * time.Hour),
				LastUsed:  time.Now(),
			}
			cm.mutex.Unlock()

			// Periodic cleanup
			if i%50 == 49 {
				cm.CleanupExpiredSessions()
			}
		}

		// Force cleanup after each wave
		cm.CleanupExpiredSessions()

		// Check session count doesn't exceed limits
		cm.mutex.RLock()
		currentCount := len(cm.sessionMap)
		cm.mutex.RUnlock()

		if currentCount > maxSessions {
			t.Errorf("Session count exceeded limit in wave %d: %d > %d",
				wave, currentCount, maxSessions)
		}

		// Check memory growth is bounded
		runtime.GC()
		var current runtime.MemStats
		runtime.ReadMemStats(&current)
		memoryGrowth := current.Alloc - baseline.Alloc

		// Memory should not grow linearly with total sessions created
		maxExpectedMemory := uint64(maxSessions * 3072) // 3KB per session with overhead
		if memoryGrowth > maxExpectedMemory {
			t.Errorf("Memory growth exceeded bounds in wave %d: %d bytes > %d expected",
				wave, memoryGrowth, maxExpectedMemory)
		}

		t.Logf("Wave %d: Sessions: %d, Memory growth: %d bytes",
			wave, currentCount, memoryGrowth)
	}
}

// TestSessionMapBounds_LRUEvictionOrder tests that LRU eviction maintains correct order
func TestSessionMapBounds_LRUEvictionOrder(t *testing.T) {
	cm := NewChunkManager(nil)
	cm.maxSessions = 10

	// Create sessions with known access patterns
	sessionOrder := make([]string, 0, 15)

	// Create initial sessions
	for i := 0; i < 15; i++ {
		sessionKey := generateOrderedSessionKey(i)
		sessionOrder = append(sessionOrder, sessionKey)

		cm.mutex.Lock()
		cm.sessionMap[sessionKey] = &SessionEntry{
			Session:   &sessions.Session{Values: make(map[interface{}]interface{})},
			ExpiresAt: time.Now().Add(24 * time.Hour),
			LastUsed:  time.Now().Add(time.Duration(-i) * time.Minute), // Older sessions have earlier LastUsed
		}
		cm.mutex.Unlock()
	}

	// Force eviction
	cm.CleanupExpiredSessions()

	// Check that oldest sessions were evicted
	cm.mutex.RLock()
	remainingSessions := make([]string, 0, len(cm.sessionMap))
	for key := range cm.sessionMap {
		remainingSessions = append(remainingSessions, key)
	}
	cm.mutex.RUnlock()

	// Should have exactly maxSessions remaining
	if len(remainingSessions) != cm.maxSessions {
		t.Errorf("Incorrect number of sessions after eviction: got %d, expected %d",
			len(remainingSessions), cm.maxSessions)
	}

	// Check that the most recently used sessions remain
	// (sessions with lower indices have more recent LastUsed times)
	expectedRemaining := sessionOrder[:cm.maxSessions]
	for _, expectedKey := range expectedRemaining {
		found := false
		for _, remainingKey := range remainingSessions {
			if remainingKey == expectedKey {
				found = true
				break
			}
		}
		if !found {
			t.Errorf("Expected session %s to remain after LRU eviction", expectedKey)
		}
	}
}

// Helper functions for generating unique session keys

func generateSessionKey(id int) string {
	return "session_" + strings.Repeat("0", 5-len(string(rune(id)))) + string(rune('0'+id%10))
}

func generateExpiredSessionKey(id int) string {
	return "expired_session_" + strings.Repeat("0", 5-len(string(rune(id)))) + string(rune('0'+id%10))
}

func generateConcurrentSessionKey(goroutineID, sessionID int) string {
	return generateSessionKey(goroutineID*1000 + sessionID)
}

func generateWaveSessionKey(wave, id int) string {
	return "wave_" + string(rune('0'+wave)) + "_" + generateSessionKey(id)
}

func generateOrderedSessionKey(id int) string {
	return "ordered_" + strings.Repeat("0", 5-len(string(rune(id)))) + string(rune('0'+id%10))
}

// BenchmarkSessionMapBounds_EvictionPerformance benchmarks the performance of session eviction
func BenchmarkSessionMapBounds_EvictionPerformance(b *testing.B) {
	cm := NewChunkManager(nil)
	cm.maxSessions = 1000

	// Pre-populate with sessions at capacity
	for i := 0; i < cm.maxSessions; i++ {
		sessionKey := generateSessionKey(i)
		cm.mutex.Lock()
		cm.sessionMap[sessionKey] = &SessionEntry{
			Session:   &sessions.Session{Values: make(map[interface{}]interface{})},
			ExpiresAt: time.Now().Add(24 * time.Hour),
			LastUsed:  time.Now().Add(time.Duration(-i) * time.Minute),
		}
		cm.mutex.Unlock()
	}

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		// Add session that will trigger eviction
		sessionKey := generateSessionKey(cm.maxSessions + i)
		cm.mutex.Lock()
		cm.sessionMap[sessionKey] = &SessionEntry{
			Session:   &sessions.Session{Values: make(map[interface{}]interface{})},
			ExpiresAt: time.Now().Add(24 * time.Hour),
			LastUsed:  time.Now(),
		}
		cm.mutex.Unlock()

		// Force eviction
		cm.CleanupExpiredSessions()
	}
}

// BenchmarkSessionMapBounds_ConcurrentAccess benchmarks concurrent session access with bounds checking
func BenchmarkSessionMapBounds_ConcurrentAccess(b *testing.B) {
	cm := NewChunkManager(nil)
	cm.maxSessions = 500

	// Pre-populate sessions
	for i := 0; i < cm.maxSessions/2; i++ {
		sessionKey := generateSessionKey(i)
		cm.mutex.Lock()
		cm.sessionMap[sessionKey] = &SessionEntry{
			Session:   &sessions.Session{Values: make(map[interface{}]interface{})},
			ExpiresAt: time.Now().Add(24 * time.Hour),
			LastUsed:  time.Now(),
		}
		cm.mutex.Unlock()
	}

	b.ResetTimer()

	b.RunParallel(func(pb *testing.PB) {
		i := 0
		for pb.Next() {
			sessionKey := generateSessionKey(i)

			// Mix of operations: create, cleanup, access
			switch i % 3 {
			case 0:
				cm.mutex.Lock()
				cm.sessionMap[sessionKey] = &SessionEntry{
					Session:   &sessions.Session{Values: make(map[interface{}]interface{})},
					ExpiresAt: time.Now().Add(24 * time.Hour),
					LastUsed:  time.Now(),
				}
				cm.mutex.Unlock()
			case 1:
				cm.CleanupExpiredSessions()
			case 2:
				cm.mutex.RLock()
				_ = len(cm.sessionMap)
				cm.mutex.RUnlock()
			}
			i++
		}
	})
}

// TestEstimateChunkCount tests the EstimateChunkCount function
func TestEstimateChunkCount(t *testing.T) {
	cs := NewChunkSerializer(nil)

	tests := []struct {
		name        string
		tokenLength int
		chunkSize   int
		expected    int
	}{
		{
			name:        "Single chunk",
			tokenLength: 1000,
			chunkSize:   1200,
			expected:    1,
		},
		{
			name:        "Exactly two chunks",
			tokenLength: 2400,
			chunkSize:   1200,
			expected:    2,
		},
		{
			name:        "Three chunks with remainder",
			tokenLength: 2500,
			chunkSize:   1200,
			expected:    3,
		},
		{
			name:        "Zero chunk size defaults to maxCookieSize",
			tokenLength: 1300,
			chunkSize:   0,
			expected:    2, // 1300 / 1200 = 1.083... = 2 chunks
		},
		{
			name:        "Large token many chunks",
			tokenLength: 10000,
			chunkSize:   800,
			expected:    13, // 10000 / 800 = 12.5 = 13 chunks
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := cs.EstimateChunkCount(tt.tokenLength, tt.chunkSize)
			if result != tt.expected {
				t.Errorf("EstimateChunkCount(%d, %d) = %d; expected %d",
					tt.tokenLength, tt.chunkSize, result, tt.expected)
			}
		})
	}
}

// TestMaxTokenSizeForChunks tests the MaxTokenSizeForChunks function
func TestMaxTokenSizeForChunks(t *testing.T) {
	cs := NewChunkSerializer(nil)

	tests := []struct {
		name      string
		maxChunks int
		chunkSize int
		expected  int
	}{
		{
			name:      "Single chunk",
			maxChunks: 1,
			chunkSize: 1200,
			expected:  1200,
		},
		{
			name:      "Multiple chunks",
			maxChunks: 5,
			chunkSize: 1000,
			expected:  5000,
		},
		{
			name:      "Zero chunk size defaults to maxCookieSize",
			maxChunks: 3,
			chunkSize: 0,
			expected:  3600, // 3 * 1200
		},
		{
			name:      "Large configuration",
			maxChunks: 25,
			chunkSize: 1200,
			expected:  30000,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := cs.MaxTokenSizeForChunks(tt.maxChunks, tt.chunkSize)
			if result != tt.expected {
				t.Errorf("MaxTokenSizeForChunks(%d, %d) = %d; expected %d",
					tt.maxChunks, tt.chunkSize, result, tt.expected)
			}
		})
	}
}

// TestValidateJWTContent tests JWT content validation
func TestValidateJWTContent(t *testing.T) {
	validator := NewTokenValidator()
	config := IDTokenConfig

	tests := []struct {
		name        string
		token       string
		description string
		expectError bool
	}{
		{
			name:        "Valid JWT with required ID token claims",
			token:       "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2V4YW1wbGUuY29tIiwic3ViIjoiMTIzNDU2Nzg5MCIsImF1ZCI6ImNsaWVudElkIiwiZXhwIjoxNjQ2MDY0MDAwLCJpYXQiOjE2NDYwNjA0MDB9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c", // trufflehog:ignore
			expectError: false,
			description: "JWT with all required ID token claims should pass",
		},
		{
			name:        "JWT missing required claims",
			token:       "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c", // trufflehog:ignore
			expectError: true,
			description: "JWT missing required claims should fail",
		},
		{
			name:        "JWT with invalid structure",
			token:       "invalid.token",
			expectError: true,
			description: "JWT with wrong number of parts should fail",
		},
		{
			name:        "Empty JWT",
			token:       "",
			expectError: true,
			description: "Empty JWT should fail at JWT content level",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := validator.validateJWTContent(tt.token, config)
			if tt.expectError && err == nil {
				t.Errorf("Expected error for %s, but got none", tt.description)
			}
			if !tt.expectError && err != nil {
				t.Errorf("Expected no error for %s, but got: %v", tt.description, err)
			}
		})
	}
}

// TestValidateJWTHeader tests JWT header validation
func TestValidateJWTHeader(t *testing.T) {
	validator := NewTokenValidator()
	config := IDTokenConfig

	tests := []struct {
		name        string
		header      string
		description string
		expectError bool
	}{
		{
			name:        "Valid JWT header",
			header:      "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9", // {"alg":"RS256","typ":"JWT"}
			expectError: false,
			description: "Valid JWT header with alg and typ",
		},
		{
			name:        "Header missing alg",
			header:      "eyJ0eXAiOiJKV1QifQ", // {"typ":"JWT"}
			expectError: true,
			description: "Header missing algorithm should fail",
		},
		{
			name:        "Header missing typ",
			header:      "eyJhbGciOiJSUzI1NiJ9", // {"alg":"RS256"}
			expectError: true,
			description: "Header missing type should fail",
		},
		{
			name:        "Invalid base64 header",
			header:      "invalid_base64!",
			expectError: true,
			description: "Invalid base64 should fail",
		},
		{
			name:        "Invalid JSON header",
			header:      "aW52YWxpZCBqc29u", // "invalid json"
			expectError: true,
			description: "Invalid JSON should fail",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := validator.validateJWTHeader(tt.header, config)
			if tt.expectError && err == nil {
				t.Errorf("Expected error for %s, but got none", tt.description)
			}
			if !tt.expectError && err != nil {
				t.Errorf("Expected no error for %s, but got: %v", tt.description, err)
			}
		})
	}
}

// TestValidateJWTPayload tests JWT payload validation
func TestValidateJWTPayload(t *testing.T) {
	validator := NewTokenValidator()

	tests := []struct {
		name        string
		payload     string
		description string
		config      TokenConfig
		expectError bool
	}{
		{
			name:        "Valid ID token payload",
			payload:     "eyJpc3MiOiJodHRwczovL2V4YW1wbGUuY29tIiwic3ViIjoiMTIzNDU2Nzg5MCIsImF1ZCI6ImNsaWVudElkIiwiZXhwIjoxNjQ2MDY0MDAwLCJpYXQiOjE2NDYwNjA0MDB9", // Required ID token claims
			config:      IDTokenConfig,
			expectError: false,
			description: "Valid ID token with required claims",
		},
		{
			name:        "ID token missing required claims",
			payload:     "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0", // {"sub":"1234567890","name":"John Doe"}
			config:      IDTokenConfig,
			expectError: true,
			description: "ID token missing required claims should fail",
		},
		{
			name:        "Access token payload",
			payload:     "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0", // {"sub":"1234567890","name":"John Doe"}
			config:      AccessTokenConfig,
			expectError: false,
			description: "Access token doesn't require specific claims",
		},
		{
			name:        "Invalid base64 payload",
			payload:     "invalid_base64!",
			config:      IDTokenConfig,
			expectError: true,
			description: "Invalid base64 should fail",
		},
		{
			name:        "Invalid JSON payload",
			payload:     "aW52YWxpZCBqc29u", // "invalid json"
			config:      IDTokenConfig,
			expectError: true,
			description: "Invalid JSON should fail",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := validator.validateJWTPayload(tt.payload, tt.config)
			if tt.expectError && err == nil {
				t.Errorf("Expected error for %s, but got none", tt.description)
			}
			if !tt.expectError && err != nil {
				t.Errorf("Expected no error for %s, but got: %v", tt.description, err)
			}
		})
	}
}

// TestValidateJWTSignature tests JWT signature validation
func TestValidateJWTSignature(t *testing.T) {
	validator := NewTokenValidator()
	config := IDTokenConfig

	tests := []struct {
		name        string
		signature   string
		description string
		expectError bool
	}{
		{
			name:        "Valid signature",
			signature:   "SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
			expectError: false,
			description: "Valid base64URL signature",
		},
		{
			name:        "Empty signature",
			signature:   "",
			expectError: true,
			description: "Empty signature should fail",
		},
		{
			name:        "Invalid base64URL signature",
			signature:   "invalid_base64!@#",
			expectError: true,
			description: "Invalid base64URL should fail",
		},
		{
			name:        "Valid signature with padding",
			signature:   "dGVzdA==",
			expectError: false,
			description: "Base64 with padding should work",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := validator.validateJWTSignature(tt.signature, config)
			if tt.expectError && err == nil {
				t.Errorf("Expected error for %s, but got none", tt.description)
			}
			if !tt.expectError && err != nil {
				t.Errorf("Expected no error for %s, but got: %v", tt.description, err)
			}
		})
	}
}

// TestValidateChunkStructure tests chunk structure validation
func TestValidateChunkStructure(t *testing.T) {
	validator := NewTokenValidator()
	config := AccessTokenConfig

	tests := []struct {
		name        string
		description string
		chunks      []ChunkData
		expectError bool
	}{
		{
			name: "Valid chunk structure",
			chunks: []ChunkData{
				{Index: 0, Total: 2, Content: "part1", Checksum: "checksum1"},
				{Index: 1, Total: 2, Content: "part2", Checksum: "checksum2"},
			},
			expectError: false,
			description: "Valid ordered chunks",
		},
		{
			name:        "Empty chunks",
			chunks:      []ChunkData{},
			expectError: true,
			description: "Empty chunk list should fail",
		},
		{
			name: "Too many chunks",
			chunks: func() []ChunkData {
				chunks := make([]ChunkData, AccessTokenConfig.MaxChunks+1)
				for i := range chunks {
					chunks[i] = ChunkData{Index: i, Total: len(chunks), Content: "content", Checksum: "checksum"}
				}
				return chunks
			}(),
			expectError: true,
			description: "Too many chunks should fail",
		},
		{
			name: "Duplicate chunk indices",
			chunks: []ChunkData{
				{Index: 0, Total: 2, Content: "part1", Checksum: "checksum1"},
				{Index: 0, Total: 2, Content: "part2", Checksum: "checksum2"},
			},
			expectError: true,
			description: "Duplicate indices should fail",
		},
		{
			name: "Missing chunk index",
			chunks: []ChunkData{
				{Index: 0, Total: 3, Content: "part1", Checksum: "checksum1"},
				{Index: 2, Total: 3, Content: "part3", Checksum: "checksum3"},
			},
			expectError: true,
			description: "Missing chunk index should fail",
		},
		{
			name: "Inconsistent total count",
			chunks: []ChunkData{
				{Index: 0, Total: 2, Content: "part1", Checksum: "checksum1"},
				{Index: 1, Total: 3, Content: "part2", Checksum: "checksum2"},
			},
			expectError: true,
			description: "Inconsistent total should fail",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := validator.ValidateChunkStructure(tt.chunks, config)
			if tt.expectError && err == nil {
				t.Errorf("Expected error for %s, but got none", tt.description)
			}
			if !tt.expectError && err != nil {
				t.Errorf("Expected no error for %s, but got: %v", tt.description, err)
			}
		})
	}
}

// TestValidateChunkData tests individual chunk data validation
func TestValidateChunkData(t *testing.T) {
	validator := NewTokenValidator()
	config := AccessTokenConfig

	tests := []struct {
		chunk         ChunkData
		name          string
		description   string
		expectedTotal int
		expectError   bool
	}{
		{
			name:          "Valid chunk data",
			chunk:         ChunkData{Index: 0, Total: 2, Content: "content", Checksum: "checksum"},
			expectedTotal: 2,
			expectError:   false,
			description:   "Valid chunk should pass",
		},
		{
			name:          "Negative index",
			chunk:         ChunkData{Index: -1, Total: 2, Content: "content", Checksum: "checksum"},
			expectedTotal: 2,
			expectError:   true,
			description:   "Negative index should fail",
		},
		{
			name:          "Inconsistent total",
			chunk:         ChunkData{Index: 0, Total: 3, Content: "content", Checksum: "checksum"},
			expectedTotal: 2,
			expectError:   true,
			description:   "Inconsistent total should fail",
		},
		{
			name:          "Index exceeds total",
			chunk:         ChunkData{Index: 2, Total: 2, Content: "content", Checksum: "checksum"},
			expectedTotal: 2,
			expectError:   true,
			description:   "Index exceeding total should fail",
		},
		{
			name:          "Empty content",
			chunk:         ChunkData{Index: 0, Total: 2, Content: "", Checksum: "checksum"},
			expectedTotal: 2,
			expectError:   true,
			description:   "Empty content should fail",
		},
		{
			name:          "Empty checksum",
			chunk:         ChunkData{Index: 0, Total: 2, Content: "content", Checksum: ""},
			expectedTotal: 2,
			expectError:   true,
			description:   "Empty checksum should fail",
		},
		{
			name: "Chunk too large",
			chunk: ChunkData{
				Index:    0,
				Total:    2,
				Content:  strings.Repeat("x", config.MaxChunkSize+1),
				Checksum: "checksum",
			},
			expectedTotal: 2,
			expectError:   true,
			description:   "Oversized chunk should fail",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := validator.validateChunkData(tt.chunk, tt.expectedTotal, config)
			if tt.expectError && err == nil {
				t.Errorf("Expected error for %s, but got none", tt.description)
			}
			if !tt.expectError && err != nil {
				t.Errorf("Expected no error for %s, but got: %v", tt.description, err)
			}
		})
	}
}

// TestChunkErrorMethod tests the Error method of ChunkError
func TestChunkErrorMethod(t *testing.T) {
	tests := []struct {
		name     string
		error    *ChunkError
		expected string
	}{
		{
			name: "Basic chunk error",
			error: &ChunkError{
				Type:    "access",
				Reason:  "too large",
				Details: "chunk exceeds maximum size",
			},
			expected: "access chunk error: too large - chunk exceeds maximum size",
		},
		{
			name: "Validation chunk error",
			error: &ChunkError{
				Type:    "id",
				Reason:  "missing chunk",
				Details: "chunk 2 is missing from sequence",
			},
			expected: "id chunk error: missing chunk - chunk 2 is missing from sequence",
		},
		{
			name: "Empty fields",
			error: &ChunkError{
				Type:    "",
				Reason:  "",
				Details: "",
			},
			expected: " chunk error:  - ",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := tt.error.Error()
			if result != tt.expected {
				t.Errorf("ChunkError.Error() = %q; expected %q", result, tt.expected)
			}
		})
	}
}

// TestValidationErrorMethod tests the Error method of ValidationError
func TestValidationErrorMethod(t *testing.T) {
	tests := []struct {
		name     string
		error    *ValidationError
		expected string
	}{
		{
			name: "Token validation error",
			error: &ValidationError{
				Type:    "access",
				Reason:  "invalid format",
				Details: "token must be valid JWT",
			},
			expected: "access validation error: invalid format - token must be valid JWT",
		},
		{
			name: "Size validation error",
			error: &ValidationError{
				Type:    "refresh",
				Reason:  "too large",
				Details: "token size exceeds 50KB limit",
			},
			expected: "refresh validation error: too large - token size exceeds 50KB limit",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := tt.error.Error()
			if result != tt.expected {
				t.Errorf("ValidationError.Error() = %q; expected %q", result, tt.expected)
			}
		})
	}
}

// TestGetToken tests the main GetToken function
func TestGetToken(t *testing.T) {
	cm := NewChunkManager(nil)

	tests := []struct {
		mainSession   *sessions.Session
		chunks        map[int]*sessions.Session
		name          string
		expectedToken string
		description   string
		config        TokenConfig
		expectError   bool
	}{
		{
			name: "Token from main session",
			mainSession: &sessions.Session{
				Values: map[interface{}]interface{}{
					"access_token": "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ",
				},
			},
			chunks:        nil,
			config:        AccessTokenConfig,
			expectedToken: "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ",
			expectError:   false,
			description:   "Should retrieve token from main session",
		},
		{
			name:          "No token in main session, no chunks",
			mainSession:   &sessions.Session{Values: map[interface{}]interface{}{}},
			chunks:        map[int]*sessions.Session{},
			config:        AccessTokenConfig,
			expectedToken: "",
			expectError:   false,
			description:   "Should return empty token when no data available",
		},
		{
			name:        "Token from chunks",
			mainSession: &sessions.Session{Values: map[interface{}]interface{}{}},
			chunks: map[int]*sessions.Session{
				0: {Values: map[interface{}]interface{}{"value": "abcdefghijklmnopqrstuvwxyz"}},
				1: {Values: map[interface{}]interface{}{"value": "0123456789ABCDEFGHIJKLMNOP"}},
			},
			config:        AccessTokenConfig,
			expectedToken: "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOP",
			expectError:   false,
			description:   "Should reconstruct token from chunks",
		},
		{
			name:        "Too many chunks",
			mainSession: &sessions.Session{Values: map[interface{}]interface{}{}},
			chunks: func() map[int]*sessions.Session {
				chunks := make(map[int]*sessions.Session)
				for i := 0; i <= AccessTokenConfig.MaxChunks; i++ {
					chunks[i] = &sessions.Session{Values: map[interface{}]interface{}{"value": "chunk"}}
				}
				return chunks
			}(),
			config:        AccessTokenConfig,
			expectedToken: "",
			expectError:   true,
			description:   "Should fail with too many chunks",
		},
	}

	// Mock compressor
	compressor := &mockTokenCompressor{}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := cm.GetToken(tt.mainSession, tt.chunks, tt.config, compressor)

			if tt.expectError && result.Error == nil {
				t.Errorf("Expected error for %s, but got none", tt.description)
			}
			if !tt.expectError && result.Error != nil {
				t.Errorf("Expected no error for %s, but got: %v", tt.description, result.Error)
			}
			if result.Token != tt.expectedToken {
				t.Errorf("Expected token %q, got %q for %s", tt.expectedToken, result.Token, tt.description)
			}
		})
	}
}

// TestStoreSessionGetSession tests session storage and retrieval
func TestStoreSessionGetSession(t *testing.T) {
	cm := NewChunkManager(nil)

	// Test storing and retrieving a session
	key := "test_session_key"
	session := &sessions.Session{Values: map[interface{}]interface{}{"test": "value"}}

	// Store session
	cm.StoreSession(key, session)

	// Retrieve session
	retrieved := cm.GetSession(key)
	if retrieved == nil {
		t.Error("Expected to retrieve stored session, but got nil")
	}

	if retrieved != session {
		t.Error("Retrieved session does not match stored session")
	}

	// Test retrieving non-existent session
	nonExistent := cm.GetSession("non_existent_key")
	if nonExistent != nil {
		t.Error("Expected nil for non-existent session, but got a session")
	}

	// Test session limit enforcement
	cm.maxSessions = 2
	for i := 0; i < 5; i++ {
		key := fmt.Sprintf("session_%d", i)
		session := &sessions.Session{Values: map[interface{}]interface{}{"id": i}}
		cm.StoreSession(key, session)
	}

	cm.mutex.RLock()
	sessionCount := len(cm.sessionMap)
	cm.mutex.RUnlock()

	if sessionCount > cm.maxSessions {
		t.Errorf("Session count %d exceeds limit %d", sessionCount, cm.maxSessions)
	}
}

// TestNoOpLogger tests the no-op logger implementation
func TestNoOpLogger(t *testing.T) {
	logger := NewNoOpLogger()

	// Test all methods (they should not panic or error)
	logger.Debug("test message")
	logger.Debugf("test format %s", "message")
	logger.Error("test error")
	logger.Errorf("test error %s", "formatted")

	// Since these are no-op methods, we mainly test that they don't panic
	// The fact that the test completes successfully indicates they work
}

// TestSerializeTokenToChunks tests token serialization
func TestSerializeTokenToChunks(t *testing.T) {
	cs := NewChunkSerializer(NewNoOpLogger())
	config := AccessTokenConfig

	tests := []struct {
		name        string
		token       string
		description string
		expectError bool
	}{
		{
			name:        "Valid token serialization",
			token:       strings.Repeat("a", 2500), // Larger than single chunk
			expectError: false,
			description: "Should serialize large token into chunks",
		},
		{
			name:        "Empty token",
			token:       "",
			expectError: true,
			description: "Should fail with empty token",
		},
		{
			name:        "Token too short",
			token:       "abc", // Less than config.MinLength
			expectError: true,
			description: "Should fail with too short token",
		},
		{
			name:        "Token too long",
			token:       strings.Repeat("x", config.MaxLength+1),
			expectError: true,
			description: "Should fail with oversized token",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			chunks, err := cs.SerializeTokenToChunks(tt.token, config)

			if tt.expectError && err == nil {
				t.Errorf("Expected error for %s, but got none", tt.description)
			}
			if !tt.expectError && err != nil {
				t.Errorf("Expected no error for %s, but got: %v", tt.description, err)
			}

			if !tt.expectError && len(chunks) > 0 {
				// Verify chunk structure
				expectedChunks := len(chunks)
				for _, chunk := range chunks {
					if chunk.Total != expectedChunks {
						t.Errorf("Chunk total mismatch: got %d, expected %d", chunk.Total, expectedChunks)
					}
					if chunk.Content == "" {
						t.Error("Chunk content should not be empty")
					}
					if chunk.Checksum == "" {
						t.Error("Chunk checksum should not be empty")
					}
				}
			}
		})
	}
}

// TestDeserializeTokenFromChunks tests token deserialization
func TestDeserializeTokenFromChunks(t *testing.T) {
	cs := NewChunkSerializer(NewNoOpLogger())
	config := AccessTokenConfig

	// First serialize a token to get valid chunks
	originalToken := strings.Repeat("abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOP", 40) // Make it large enough for multiple chunks
	chunks, err := cs.SerializeTokenToChunks(originalToken, config)
	if err != nil {
		t.Fatalf("Failed to serialize token for test: %v", err)
	}

	tests := []struct {
		name          string
		expectedToken string
		description   string
		chunks        []ChunkData
		expectError   bool
	}{
		{
			name:          "Valid chunks deserialization",
			chunks:        chunks,
			expectedToken: originalToken,
			expectError:   false,
			description:   "Should deserialize chunks back to original token",
		},
		{
			name:          "Empty chunks",
			chunks:        []ChunkData{},
			expectedToken: "",
			expectError:   true,
			description:   "Should fail with empty chunks",
		},
		{
			name: "Too many chunks",
			chunks: func() []ChunkData {
				many := make([]ChunkData, config.MaxChunks+1)
				for i := range many {
					many[i] = ChunkData{Index: i, Total: len(many), Content: "content", Checksum: "checksum"}
				}
				return many
			}(),
			expectedToken: "",
			expectError:   true,
			description:   "Should fail with too many chunks",
		},
		{
			name: "Inconsistent chunk totals",
			chunks: []ChunkData{
				{Index: 0, Total: 2, Content: "part1", Checksum: cs.calculateChecksum("part1")},
				{Index: 1, Total: 3, Content: "part2", Checksum: cs.calculateChecksum("part2")}, // Different total
			},
			expectedToken: "",
			expectError:   true,
			description:   "Should fail with inconsistent totals",
		},
		{
			name: "Missing chunk",
			chunks: []ChunkData{
				{Index: 0, Total: 3, Content: "part1", Checksum: cs.calculateChecksum("part1")},
				{Index: 2, Total: 3, Content: "part3", Checksum: cs.calculateChecksum("part3")}, // Missing index 1
			},
			expectedToken: "",
			expectError:   true,
			description:   "Should fail with missing chunk",
		},
		{
			name: "Invalid checksum",
			chunks: []ChunkData{
				{Index: 0, Total: 2, Content: "part1", Checksum: "invalid_checksum"},
				{Index: 1, Total: 2, Content: "part2", Checksum: cs.calculateChecksum("part2")},
			},
			expectedToken: "",
			expectError:   true,
			description:   "Should fail with invalid checksum",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			token, err := cs.DeserializeTokenFromChunks(tt.chunks, config)

			if tt.expectError && err == nil {
				t.Errorf("Expected error for %s, but got none", tt.description)
			}
			if !tt.expectError && err != nil {
				t.Errorf("Expected no error for %s, but got: %v", tt.description, err)
			}
			if token != tt.expectedToken {
				t.Errorf("Expected token length %d, got %d for %s", len(tt.expectedToken), len(token), tt.description)
			}
		})
	}
}

// TestEncodeDecodeChunk tests chunk encoding and decoding
func TestEncodeDecodeChunk(t *testing.T) {
	cs := NewChunkSerializer(NewNoOpLogger())

	tests := []struct {
		chunk       ChunkData
		name        string
		description string
		expectError bool
	}{
		{
			name: "Valid chunk encoding/decoding",
			chunk: ChunkData{
				Index:    0,
				Total:    2,
				Content:  "test_content",
				Checksum: "test_checksum",
			},
			expectError: false,
			description: "Should encode and decode chunk successfully",
		},
		{
			name: "Chunk with special characters",
			chunk: ChunkData{
				Index:    1,
				Total:    3,
				Content:  "content:with:colons",
				Checksum: "checksum_123",
			},
			expectError: false,
			description: "Should handle special characters in content",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Encode chunk
			encoded, err := cs.EncodeChunk(tt.chunk)
			if tt.expectError && err == nil {
				t.Errorf("Expected encoding error for %s, but got none", tt.description)
			}
			if !tt.expectError && err != nil {
				t.Errorf("Expected no encoding error for %s, but got: %v", tt.description, err)
			}

			if !tt.expectError && encoded != "" {
				// Decode chunk
				decoded, err := cs.DecodeChunk(encoded)
				if err != nil {
					t.Errorf("Expected no decoding error for %s, but got: %v", tt.description, err)
				}

				// Verify decoded chunk matches original
				if decoded.Index != tt.chunk.Index {
					t.Errorf("Index mismatch: got %d, expected %d", decoded.Index, tt.chunk.Index)
				}
				if decoded.Total != tt.chunk.Total {
					t.Errorf("Total mismatch: got %d, expected %d", decoded.Total, tt.chunk.Total)
				}
				if decoded.Content != tt.chunk.Content {
					t.Errorf("Content mismatch: got %q, expected %q", decoded.Content, tt.chunk.Content)
				}
				if decoded.Checksum != tt.chunk.Checksum {
					t.Errorf("Checksum mismatch: got %q, expected %q", decoded.Checksum, tt.chunk.Checksum)
				}
			}
		})
	}

	// Test decoding invalid data
	invalidTests := []struct {
		name        string
		encoded     string
		description string
	}{
		{
			name:        "Invalid base64",
			encoded:     "invalid_base64!",
			description: "Should fail with invalid base64",
		},
		{
			name:        "Wrong format",
			encoded:     "dGVzdA==", // "test" in base64, but wrong format
			description: "Should fail with wrong format",
		},
	}

	for _, tt := range invalidTests {
		t.Run(tt.name, func(t *testing.T) {
			_, err := cs.DecodeChunk(tt.encoded)
			if err == nil {
				t.Errorf("Expected error for %s, but got none", tt.description)
			}
		})
	}
}

// TestValidateChunkIntegrity tests chunk integrity validation
func TestValidateChunkIntegrity(t *testing.T) {
	cs := NewChunkSerializer(NewNoOpLogger())

	tests := []struct {
		chunk       ChunkData
		name        string
		description string
		expectError bool
	}{
		{
			name: "Valid chunk integrity",
			chunk: ChunkData{
				Index:    0,
				Total:    2,
				Content:  "test_content",
				Checksum: cs.calculateChecksum("test_content"),
			},
			expectError: false,
			description: "Should pass integrity check",
		},
		{
			name: "Negative index",
			chunk: ChunkData{
				Index:    -1,
				Total:    2,
				Content:  "test_content",
				Checksum: cs.calculateChecksum("test_content"),
			},
			expectError: true,
			description: "Should fail with negative index",
		},
		{
			name: "Invalid total",
			chunk: ChunkData{
				Index:    0,
				Total:    0,
				Content:  "test_content",
				Checksum: cs.calculateChecksum("test_content"),
			},
			expectError: true,
			description: "Should fail with zero total",
		},
		{
			name: "Index exceeds total",
			chunk: ChunkData{
				Index:    2,
				Total:    2,
				Content:  "test_content",
				Checksum: cs.calculateChecksum("test_content"),
			},
			expectError: true,
			description: "Should fail with index >= total",
		},
		{
			name: "Empty content",
			chunk: ChunkData{
				Index:    0,
				Total:    2,
				Content:  "",
				Checksum: cs.calculateChecksum(""),
			},
			expectError: true,
			description: "Should fail with empty content",
		},
		{
			name: "Empty checksum",
			chunk: ChunkData{
				Index:    0,
				Total:    2,
				Content:  "test_content",
				Checksum: "",
			},
			expectError: true,
			description: "Should fail with empty checksum",
		},
		{
			name: "Invalid checksum",
			chunk: ChunkData{
				Index:    0,
				Total:    2,
				Content:  "test_content",
				Checksum: "invalid_checksum",
			},
			expectError: true,
			description: "Should fail with wrong checksum",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := cs.ValidateChunkIntegrity(tt.chunk)
			if tt.expectError && err == nil {
				t.Errorf("Expected error for %s, but got none", tt.description)
			}
			if !tt.expectError && err != nil {
				t.Errorf("Expected no error for %s, but got: %v", tt.description, err)
			}
		})
	}
}

// TestCalculateChecksum tests checksum calculation
func TestCalculateChecksum(t *testing.T) {
	cs := NewChunkSerializer(NewNoOpLogger())

	tests := []struct {
		name     string
		content  string
		expected string
	}{
		{
			name:     "Empty content",
			content:  "",
			expected: "empty",
		},
		{
			name:     "Single character",
			content:  "a",
			expected: "len1_first97",
		},
		{
			name:     "Two characters",
			content:  "ab",
			expected: "len2_first97_last98",
		},
		{
			name:     "Longer content",
			content:  "test_content",
			expected: "len12_first116_last116",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := cs.calculateChecksum(tt.content)
			if result != tt.expected {
				t.Errorf("calculateChecksum(%q) = %q; expected %q", tt.content, result, tt.expected)
			}
		})
	}
}

// Mock token compressor for testing
type mockTokenCompressor struct{}

func (m *mockTokenCompressor) CompressToken(token string) string {
	// Simple mock - just return the original token
	return token
}

func (m *mockTokenCompressor) DecompressToken(compressed string) string {
	// Simple mock - just return the original token
	return compressed
}
