//go:build cgo
// +build cgo

package audio

import (
	"runtime"
	"sync"
	"sync/atomic"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

// TestPerformanceCriticalPaths tests the most frequently executed code paths
// to ensure they remain efficient and don't interfere with KVM functionality
func TestPerformanceCriticalPaths(t *testing.T) {
	tests := []struct {
		name     string
		testFunc func(t *testing.T)
	}{
		{"AudioFrameProcessingLatency", testAudioFrameProcessingLatency},
		{"MetricsUpdateOverhead", testMetricsUpdateOverhead},
		{"ConfigurationAccessSpeed", testConfigurationAccessSpeed},
		{"ValidationFunctionSpeed", testValidationFunctionSpeed},
		{"MemoryAllocationPatterns", testMemoryAllocationPatterns},
		{"ConcurrentAccessPerformance", testConcurrentAccessPerformance},
		{"BufferPoolEfficiency", testBufferPoolEfficiency},
		{"AtomicOperationOverhead", testAtomicOperationOverhead},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if testing.Short() {
				t.Skip("Skipping performance test in short mode")
			}
			tt.testFunc(t)
		})
	}
}

// testAudioFrameProcessingLatency tests the latency of audio frame processing
// This is the most critical path that must not interfere with KVM
func testAudioFrameProcessingLatency(t *testing.T) {
	const (
		frameCount         = 1000
		maxLatencyPerFrame = 100 * time.Microsecond // Very strict requirement
	)

	// Create test frame data
	frameData := make([]byte, 1920) // Typical frame size
	for i := range frameData {
		frameData[i] = byte(i % 256)
	}

	// Measure frame processing latency
	start := time.Now()
	for i := 0; i < frameCount; i++ {
		// Simulate the critical path: validation + metrics update
		err := ValidateAudioFrameFast(frameData)
		require.NoError(t, err)

		// Record frame received (atomic operation)
		RecordFrameReceived(len(frameData))
	}
	elapsed := time.Since(start)

	avgLatencyPerFrame := elapsed / frameCount
	t.Logf("Average frame processing latency: %v", avgLatencyPerFrame)

	// Ensure frame processing is fast enough to not interfere with KVM
	assert.Less(t, avgLatencyPerFrame, maxLatencyPerFrame,
		"Frame processing latency %v exceeds maximum %v - may interfere with KVM",
		avgLatencyPerFrame, maxLatencyPerFrame)

	// Ensure total processing time is reasonable
	maxTotalTime := 50 * time.Millisecond
	assert.Less(t, elapsed, maxTotalTime,
		"Total processing time %v exceeds maximum %v", elapsed, maxTotalTime)
}

// testMetricsUpdateOverhead tests the overhead of metrics updates
func testMetricsUpdateOverhead(t *testing.T) {
	const iterations = 10000

	// Test RecordFrameReceived performance
	start := time.Now()
	for i := 0; i < iterations; i++ {
		RecordFrameReceived(1024)
	}
	recordLatency := time.Since(start) / iterations

	// Test GetAudioMetrics performance
	start = time.Now()
	for i := 0; i < iterations; i++ {
		_ = GetAudioMetrics()
	}
	getLatency := time.Since(start) / iterations

	t.Logf("RecordFrameReceived latency: %v", recordLatency)
	t.Logf("GetAudioMetrics latency: %v", getLatency)

	// Metrics operations should be optimized for JetKVM's ARM Cortex-A7 @ 1GHz
	// With 256MB RAM, we need to be conservative with performance expectations
	assert.Less(t, recordLatency, 50*time.Microsecond, "RecordFrameReceived too slow")
	assert.Less(t, getLatency, 20*time.Microsecond, "GetAudioMetrics too slow")
}

// testConfigurationAccessSpeed tests configuration access performance
func testConfigurationAccessSpeed(t *testing.T) {
	const iterations = 10000

	// Test GetAudioConfig performance
	start := time.Now()
	for i := 0; i < iterations; i++ {
		_ = GetAudioConfig()
	}
	configLatency := time.Since(start) / iterations

	// Test GetConfig performance
	start = time.Now()
	for i := 0; i < iterations; i++ {
		_ = GetConfig()
	}
	constantsLatency := time.Since(start) / iterations

	t.Logf("GetAudioConfig latency: %v", configLatency)
	t.Logf("GetConfig latency: %v", constantsLatency)

	// Configuration access should be very fast
	assert.Less(t, configLatency, 100*time.Nanosecond, "GetAudioConfig too slow")
	assert.Less(t, constantsLatency, 100*time.Nanosecond, "GetConfig too slow")
}

// testValidationFunctionSpeed tests validation function performance
func testValidationFunctionSpeed(t *testing.T) {
	const iterations = 10000
	frameData := make([]byte, 1920)

	// Test ValidateAudioFrameFast (most critical)
	start := time.Now()
	for i := 0; i < iterations; i++ {
		err := ValidateAudioFrameFast(frameData)
		require.NoError(t, err)
	}
	fastValidationLatency := time.Since(start) / iterations

	// Test ValidateAudioQuality
	start = time.Now()
	for i := 0; i < iterations; i++ {
		err := ValidateAudioQuality(AudioQualityMedium)
		require.NoError(t, err)
	}
	qualityValidationLatency := time.Since(start) / iterations

	// Test ValidateBufferSize
	start = time.Now()
	for i := 0; i < iterations; i++ {
		err := ValidateBufferSize(1024)
		require.NoError(t, err)
	}
	bufferValidationLatency := time.Since(start) / iterations

	t.Logf("ValidateAudioFrameFast latency: %v", fastValidationLatency)
	t.Logf("ValidateAudioQuality latency: %v", qualityValidationLatency)
	t.Logf("ValidateBufferSize latency: %v", bufferValidationLatency)

	// Validation functions optimized for ARM Cortex-A7 single core @ 1GHz
	// Conservative thresholds to ensure KVM functionality isn't impacted
	assert.Less(t, fastValidationLatency, 100*time.Microsecond, "ValidateAudioFrameFast too slow")
	assert.Less(t, qualityValidationLatency, 50*time.Microsecond, "ValidateAudioQuality too slow")
	assert.Less(t, bufferValidationLatency, 50*time.Microsecond, "ValidateBufferSize too slow")
}

// testMemoryAllocationPatterns tests memory allocation efficiency
func testMemoryAllocationPatterns(t *testing.T) {
	// Test that frequent operations don't cause excessive allocations
	var m1, m2 runtime.MemStats
	runtime.GC()
	runtime.ReadMemStats(&m1)

	// Perform operations that should minimize allocations
	for i := 0; i < 1000; i++ {
		_ = GetAudioConfig()
		_ = GetAudioMetrics()
		RecordFrameReceived(1024)
		_ = ValidateAudioQuality(AudioQualityMedium)
	}

	runtime.GC()
	runtime.ReadMemStats(&m2)

	allocations := m2.Mallocs - m1.Mallocs
	t.Logf("Memory allocations for 1000 operations: %d", allocations)

	// Should have minimal allocations for these hot path operations
	assert.Less(t, allocations, uint64(100), "Too many memory allocations in hot path")
}

// testConcurrentAccessPerformance tests performance under concurrent access
func testConcurrentAccessPerformance(t *testing.T) {
	const (
		numGoroutines          = 10
		operationsPerGoroutine = 1000
	)

	var wg sync.WaitGroup
	start := time.Now()

	// Launch concurrent goroutines performing audio operations
	for i := 0; i < numGoroutines; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			frameData := make([]byte, 1920)

			for j := 0; j < operationsPerGoroutine; j++ {
				// Simulate concurrent audio processing
				_ = ValidateAudioFrameFast(frameData)
				RecordFrameReceived(len(frameData))
				_ = GetAudioMetrics()
				_ = GetAudioConfig()
			}
		}()
	}

	wg.Wait()
	elapsed := time.Since(start)

	totalOperations := numGoroutines * operationsPerGoroutine * 4 // 4 operations per iteration
	avgLatency := elapsed / time.Duration(totalOperations)

	t.Logf("Concurrent access: %d operations in %v (avg: %v per operation)",
		totalOperations, elapsed, avgLatency)

	// Concurrent access should not significantly degrade performance
	assert.Less(t, avgLatency, 1*time.Microsecond, "Concurrent access too slow")
}

// testBufferPoolEfficiency tests buffer pool performance
func testBufferPoolEfficiency(t *testing.T) {
	// Test buffer acquisition and release performance
	const iterations = 1000

	start := time.Now()
	for i := 0; i < iterations; i++ {
		// Simulate buffer pool usage (if available)
		buffer := make([]byte, 1920) // Fallback to allocation
		_ = buffer
		// In real implementation, this would be pool.Get() and pool.Put()
	}
	elapsed := time.Since(start)

	avgLatency := elapsed / iterations
	t.Logf("Buffer allocation latency: %v per buffer", avgLatency)

	// Buffer operations should be fast
	assert.Less(t, avgLatency, 1*time.Microsecond, "Buffer allocation too slow")
}

// testAtomicOperationOverhead tests atomic operation performance
func testAtomicOperationOverhead(t *testing.T) {
	const iterations = 10000
	var counter int64

	// Test atomic increment performance
	start := time.Now()
	for i := 0; i < iterations; i++ {
		atomic.AddInt64(&counter, 1)
	}
	atomicLatency := time.Since(start) / iterations

	// Test atomic load performance
	start = time.Now()
	for i := 0; i < iterations; i++ {
		_ = atomic.LoadInt64(&counter)
	}
	loadLatency := time.Since(start) / iterations

	t.Logf("Atomic add latency: %v", atomicLatency)
	t.Logf("Atomic load latency: %v", loadLatency)

	// Atomic operations on ARM Cortex-A7 - realistic expectations
	assert.Less(t, atomicLatency, 1*time.Microsecond, "Atomic add too slow")
	assert.Less(t, loadLatency, 500*time.Nanosecond, "Atomic load too slow")
}

// TestRegressionDetection tests for performance regressions
func TestRegressionDetection(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping regression test in short mode")
	}

	// Baseline performance expectations
	baselines := map[string]time.Duration{
		"frame_processing": 100 * time.Microsecond,
		"metrics_update":   500 * time.Nanosecond,
		"config_access":    100 * time.Nanosecond,
		"validation":       200 * time.Nanosecond,
	}

	// Test frame processing
	frameData := make([]byte, 1920)
	start := time.Now()
	for i := 0; i < 100; i++ {
		_ = ValidateAudioFrameFast(frameData)
		RecordFrameReceived(len(frameData))
	}
	frameProcessingTime := time.Since(start) / 100

	// Test metrics update
	start = time.Now()
	for i := 0; i < 1000; i++ {
		RecordFrameReceived(1024)
	}
	metricsUpdateTime := time.Since(start) / 1000

	// Test config access
	start = time.Now()
	for i := 0; i < 1000; i++ {
		_ = GetAudioConfig()
	}
	configAccessTime := time.Since(start) / 1000

	// Test validation
	start = time.Now()
	for i := 0; i < 1000; i++ {
		_ = ValidateAudioQuality(AudioQualityMedium)
	}
	validationTime := time.Since(start) / 1000

	// Performance regression thresholds for JetKVM hardware:
	// - ARM Cortex-A7 @ 1GHz single core
	// - 256MB DDR3L RAM
	// - Must not interfere with primary KVM functionality
	assert.Less(t, frameProcessingTime, baselines["frame_processing"],
		"Frame processing regression: %v > %v", frameProcessingTime, baselines["frame_processing"])
	assert.Less(t, metricsUpdateTime, 100*time.Microsecond,
		"Metrics update regression: %v > 100μs", metricsUpdateTime)
	assert.Less(t, configAccessTime, 10*time.Microsecond,
		"Config access regression: %v > 10μs", configAccessTime)
	assert.Less(t, validationTime, 10*time.Microsecond,
		"Validation regression: %v > 10μs", validationTime)

	t.Logf("Performance results:")
	t.Logf("  Frame processing: %v (baseline: %v)", frameProcessingTime, baselines["frame_processing"])
	t.Logf("  Metrics update: %v (baseline: %v)", metricsUpdateTime, baselines["metrics_update"])
	t.Logf("  Config access: %v (baseline: %v)", configAccessTime, baselines["config_access"])
	t.Logf("  Validation: %v (baseline: %v)", validationTime, baselines["validation"])
}

// TestMemoryLeakDetection tests for memory leaks in critical paths
func TestMemoryLeakDetection(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping memory leak test in short mode")
	}

	var m1, m2 runtime.MemStats

	// Baseline measurement
	runtime.GC()
	runtime.ReadMemStats(&m1)

	// Perform many operations that should not leak memory
	for cycle := 0; cycle < 10; cycle++ {
		for i := 0; i < 1000; i++ {
			frameData := make([]byte, 1920)
			_ = ValidateAudioFrameFast(frameData)
			RecordFrameReceived(len(frameData))
			_ = GetAudioMetrics()
			_ = GetAudioConfig()
		}
		// Force garbage collection between cycles
		runtime.GC()
	}

	// Final measurement
	runtime.GC()
	runtime.ReadMemStats(&m2)

	memoryGrowth := int64(m2.Alloc) - int64(m1.Alloc)
	t.Logf("Memory growth after 10,000 operations: %d bytes", memoryGrowth)

	// Memory growth should be minimal (less than 1MB)
	assert.Less(t, memoryGrowth, int64(1024*1024),
		"Excessive memory growth detected: %d bytes", memoryGrowth)
}
