// Package integration provides integration tests for memory leak detection.
//
//go:build integration
// +build integration

package integration

import (
	"runtime"
	"testing"
	"time"

	"github.com/awnumar/memguard"

	"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/security/securebytes"
	"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/security/securestring"
)

// TestSecureStringMemoryLeak verifies SecureString doesn't leak memory.
func TestSecureStringMemoryLeak(t *testing.T) {
	// Force GC before measurement
	runtime.GC()
	time.Sleep(100 * time.Millisecond)

	var memBefore runtime.MemStats
	runtime.ReadMemStats(&memBefore)

	// Perform 10000 SecureString operations
	for i := 0; i < 10000; i++ {
		s, err := securestring.NewSecureString("test data that should be cleaned up properly")
		if err != nil {
			t.Fatalf("Failed to create SecureString: %v", err)
		}
		_ = s.String() // Access the string
		s.Wipe()
	}

	// Force GC after operations
	runtime.GC()
	time.Sleep(100 * time.Millisecond)

	var memAfter runtime.MemStats
	runtime.ReadMemStats(&memAfter)

	// Check for memory leak - allow up to 5MB growth for test overhead
	heapGrowth := int64(memAfter.HeapAlloc) - int64(memBefore.HeapAlloc)
	if heapGrowth > 5*1024*1024 {
		t.Errorf("Possible memory leak in SecureString: heap grew by %d bytes", heapGrowth)
	}
}

// TestSecureBytesMemoryLeak verifies SecureBytes doesn't leak memory.
func TestSecureBytesMemoryLeak(t *testing.T) {
	// Force GC before measurement
	runtime.GC()
	time.Sleep(100 * time.Millisecond)

	var memBefore runtime.MemStats
	runtime.ReadMemStats(&memBefore)

	// Perform 10000 SecureBytes operations
	for i := 0; i < 10000; i++ {
		data := make([]byte, 64)
		for j := range data {
			data[j] = byte(i % 256)
		}

		sb, err := securebytes.NewSecureBytes(data)
		if err != nil {
			t.Fatalf("Failed to create SecureBytes: %v", err)
		}
		_ = sb.Bytes() // Access the bytes
		sb.Wipe()
	}

	// Force GC after operations
	runtime.GC()
	time.Sleep(100 * time.Millisecond)

	var memAfter runtime.MemStats
	runtime.ReadMemStats(&memAfter)

	// Check for memory leak
	heapGrowth := int64(memAfter.HeapAlloc) - int64(memBefore.HeapAlloc)
	if heapGrowth > 5*1024*1024 {
		t.Errorf("Possible memory leak in SecureBytes: heap grew by %d bytes", heapGrowth)
	}
}

// TestMemguardWipeBytesEffectiveness verifies memguard.WipeBytes actually zeros memory.
func TestMemguardWipeBytesEffectiveness(t *testing.T) {
	// Create sensitive data
	sensitiveData := []byte("super-secret-password-12345")
	originalLen := len(sensitiveData)

	// Wipe the data
	memguard.WipeBytes(sensitiveData)

	// Verify all bytes are zero
	for i, b := range sensitiveData {
		if b != 0 {
			t.Errorf("Byte at position %d not wiped: got %d, expected 0", i, b)
		}
	}

	// Verify length unchanged
	if len(sensitiveData) != originalLen {
		t.Errorf("Length changed after wipe: got %d, expected %d", len(sensitiveData), originalLen)
	}
}

// TestSecureStringWipeEffectiveness verifies SecureString.Wipe() actually cleans up.
func TestSecureStringWipeEffectiveness(t *testing.T) {
	secret := "my-super-secret-token"
	ss, err := securestring.NewSecureString(secret)
	if err != nil {
		t.Fatalf("Failed to create SecureString: %v", err)
	}

	// Verify we can access the string before wiping
	if ss.String() != secret {
		t.Error("SecureString didn't store the original value correctly")
	}

	// Wipe the secure string
	ss.Wipe()

	// After wiping, String() should return empty
	if ss.String() != "" {
		t.Error("SecureString.String() should return empty after Wipe()")
	}
}

// TestLockedBufferMemoryLeak verifies LockedBuffer doesn't leak memory.
func TestLockedBufferMemoryLeak(t *testing.T) {
	// Force GC before measurement
	runtime.GC()
	time.Sleep(100 * time.Millisecond)

	var memBefore runtime.MemStats
	runtime.ReadMemStats(&memBefore)

	// Perform 10000 LockedBuffer operations
	for i := 0; i < 10000; i++ {
		buf := memguard.NewBuffer(64)
		copy(buf.Bytes(), []byte("test-data-for-locked-buffer"))
		buf.Destroy()
	}

	// Force GC after operations
	runtime.GC()
	time.Sleep(100 * time.Millisecond)

	var memAfter runtime.MemStats
	runtime.ReadMemStats(&memAfter)

	// Check for memory leak
	heapGrowth := int64(memAfter.HeapAlloc) - int64(memBefore.HeapAlloc)
	if heapGrowth > 5*1024*1024 {
		t.Errorf("Possible memory leak in LockedBuffer: heap grew by %d bytes", heapGrowth)
	}
}

// TestConcurrentSecureStringOperations tests thread safety.
func TestConcurrentSecureStringOperations(t *testing.T) {
	done := make(chan bool, 10)

	for i := 0; i < 10; i++ {
		go func(id int) {
			defer func() { done <- true }()

			for j := 0; j < 1000; j++ {
				ss, err := securestring.NewSecureString("concurrent-test-data")
				if err != nil {
					t.Errorf("Goroutine %d: Failed to create SecureString: %v", id, err)
					return
				}
				_ = ss.String()
				ss.Wipe()
			}
		}(i)
	}

	// Wait for all goroutines
	for i := 0; i < 10; i++ {
		<-done
	}
}

// TestRepeatedWipeCalls verifies multiple Wipe() calls don't panic.
func TestRepeatedWipeCalls(t *testing.T) {
	ss, err := securestring.NewSecureString("test-data")
	if err != nil {
		t.Fatalf("Failed to create SecureString: %v", err)
	}

	// Multiple wipe calls should be safe
	for i := 0; i < 10; i++ {
		err := ss.Wipe()
		if err != nil {
			t.Errorf("Wipe() call %d returned error: %v", i, err)
		}
	}
}

// Run integration tests with:
// go test -tags=integration -v ./test/integration/
