package s3api

import (
	"bytes"
	"fmt"
	"io"
	"strings"
	"testing"
	
	"github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
)

// TestSSECMultipartUpload tests SSE-C with multipart uploads
func TestSSECMultipartUpload(t *testing.T) {
	keyPair := GenerateTestSSECKey(1)
	customerKey := &SSECustomerKey{
		Algorithm: "AES256",
		Key:       keyPair.Key,
		KeyMD5:    keyPair.KeyMD5,
	}

	// Test data larger than typical part size
	testData := strings.Repeat("Hello, SSE-C multipart world! ", 1000) // ~30KB

	t.Run("Single part encryption/decryption", func(t *testing.T) {
		// Encrypt the data
		encryptedReader, iv, err := CreateSSECEncryptedReader(strings.NewReader(testData), customerKey)
		if err != nil {
			t.Fatalf("Failed to create encrypted reader: %v", err)
		}

		encryptedData, err := io.ReadAll(encryptedReader)
		if err != nil {
			t.Fatalf("Failed to read encrypted data: %v", err)
		}

		// Decrypt the data
		decryptedReader, err := CreateSSECDecryptedReader(bytes.NewReader(encryptedData), customerKey, iv)
		if err != nil {
			t.Fatalf("Failed to create decrypted reader: %v", err)
		}

		decryptedData, err := io.ReadAll(decryptedReader)
		if err != nil {
			t.Fatalf("Failed to read decrypted data: %v", err)
		}

		if string(decryptedData) != testData {
			t.Error("Decrypted data doesn't match original")
		}
	})

	t.Run("Simulated multipart upload parts", func(t *testing.T) {
		// Simulate multiple parts (each part gets encrypted separately)
		partSize := 5 * 1024 // 5KB parts
		var encryptedParts [][]byte
		var partIVs [][]byte

		for i := 0; i < len(testData); i += partSize {
			end := i + partSize
			if end > len(testData) {
				end = len(testData)
			}

			partData := testData[i:end]

			// Each part is encrypted separately in multipart uploads
			encryptedReader, iv, err := CreateSSECEncryptedReader(strings.NewReader(partData), customerKey)
			if err != nil {
				t.Fatalf("Failed to create encrypted reader for part %d: %v", i/partSize, err)
			}

			encryptedPart, err := io.ReadAll(encryptedReader)
			if err != nil {
				t.Fatalf("Failed to read encrypted part %d: %v", i/partSize, err)
			}

			encryptedParts = append(encryptedParts, encryptedPart)
			partIVs = append(partIVs, iv)
		}

		// Simulate reading back the multipart object
		var reconstructedData strings.Builder

		for i, encryptedPart := range encryptedParts {
			decryptedReader, err := CreateSSECDecryptedReader(bytes.NewReader(encryptedPart), customerKey, partIVs[i])
			if err != nil {
				t.Fatalf("Failed to create decrypted reader for part %d: %v", i, err)
			}

			decryptedPart, err := io.ReadAll(decryptedReader)
			if err != nil {
				t.Fatalf("Failed to read decrypted part %d: %v", i, err)
			}

			reconstructedData.Write(decryptedPart)
		}

		if reconstructedData.String() != testData {
			t.Error("Reconstructed multipart data doesn't match original")
		}
	})

	t.Run("Multipart with different part sizes", func(t *testing.T) {
		partSizes := []int{1024, 2048, 4096, 8192} // Various part sizes

		for _, partSize := range partSizes {
			t.Run(fmt.Sprintf("PartSize_%d", partSize), func(t *testing.T) {
				var encryptedParts [][]byte
				var partIVs [][]byte

				for i := 0; i < len(testData); i += partSize {
					end := i + partSize
					if end > len(testData) {
						end = len(testData)
					}

					partData := testData[i:end]

					encryptedReader, iv, err := CreateSSECEncryptedReader(strings.NewReader(partData), customerKey)
					if err != nil {
						t.Fatalf("Failed to create encrypted reader: %v", err)
					}

					encryptedPart, err := io.ReadAll(encryptedReader)
					if err != nil {
						t.Fatalf("Failed to read encrypted part: %v", err)
					}

					encryptedParts = append(encryptedParts, encryptedPart)
					partIVs = append(partIVs, iv)
				}

				// Verify reconstruction
				var reconstructedData strings.Builder

				for j, encryptedPart := range encryptedParts {
					decryptedReader, err := CreateSSECDecryptedReader(bytes.NewReader(encryptedPart), customerKey, partIVs[j])
					if err != nil {
						t.Fatalf("Failed to create decrypted reader: %v", err)
					}

					decryptedPart, err := io.ReadAll(decryptedReader)
					if err != nil {
						t.Fatalf("Failed to read decrypted part: %v", err)
					}

					reconstructedData.Write(decryptedPart)
				}

				if reconstructedData.String() != testData {
					t.Errorf("Reconstructed data doesn't match original for part size %d", partSize)
				}
			})
		}
	})
}

// TestSSEKMSMultipartUpload tests SSE-KMS with multipart uploads
func TestSSEKMSMultipartUpload(t *testing.T) {
	kmsKey := SetupTestKMS(t)
	defer kmsKey.Cleanup()

	// Test data larger than typical part size
	testData := strings.Repeat("Hello, SSE-KMS multipart world! ", 1000) // ~30KB
	encryptionContext := BuildEncryptionContext("test-bucket", "test-object", false)

	t.Run("Single part encryption/decryption", func(t *testing.T) {
		// Encrypt the data
		encryptedReader, sseKey, err := CreateSSEKMSEncryptedReader(strings.NewReader(testData), kmsKey.KeyID, encryptionContext)
		if err != nil {
			t.Fatalf("Failed to create encrypted reader: %v", err)
		}

		encryptedData, err := io.ReadAll(encryptedReader)
		if err != nil {
			t.Fatalf("Failed to read encrypted data: %v", err)
		}

		// Decrypt the data
		decryptedReader, err := CreateSSEKMSDecryptedReader(bytes.NewReader(encryptedData), sseKey)
		if err != nil {
			t.Fatalf("Failed to create decrypted reader: %v", err)
		}

		decryptedData, err := io.ReadAll(decryptedReader)
		if err != nil {
			t.Fatalf("Failed to read decrypted data: %v", err)
		}

		if string(decryptedData) != testData {
			t.Error("Decrypted data doesn't match original")
		}
	})

	t.Run("Simulated multipart upload parts", func(t *testing.T) {
		// Simulate multiple parts (each part might use the same or different KMS operations)
		partSize := 5 * 1024 // 5KB parts
		var encryptedParts [][]byte
		var sseKeys []*SSEKMSKey

		for i := 0; i < len(testData); i += partSize {
			end := i + partSize
			if end > len(testData) {
				end = len(testData)
			}

			partData := testData[i:end]

			// Each part might get its own data key in KMS multipart uploads
			encryptedReader, sseKey, err := CreateSSEKMSEncryptedReader(strings.NewReader(partData), kmsKey.KeyID, encryptionContext)
			if err != nil {
				t.Fatalf("Failed to create encrypted reader for part %d: %v", i/partSize, err)
			}

			encryptedPart, err := io.ReadAll(encryptedReader)
			if err != nil {
				t.Fatalf("Failed to read encrypted part %d: %v", i/partSize, err)
			}

			encryptedParts = append(encryptedParts, encryptedPart)
			sseKeys = append(sseKeys, sseKey)
		}

		// Simulate reading back the multipart object
		var reconstructedData strings.Builder

		for i, encryptedPart := range encryptedParts {
			decryptedReader, err := CreateSSEKMSDecryptedReader(bytes.NewReader(encryptedPart), sseKeys[i])
			if err != nil {
				t.Fatalf("Failed to create decrypted reader for part %d: %v", i, err)
			}

			decryptedPart, err := io.ReadAll(decryptedReader)
			if err != nil {
				t.Fatalf("Failed to read decrypted part %d: %v", i, err)
			}

			reconstructedData.Write(decryptedPart)
		}

		if reconstructedData.String() != testData {
			t.Error("Reconstructed multipart data doesn't match original")
		}
	})

	t.Run("Multipart consistency checks", func(t *testing.T) {
		// Test that all parts use the same KMS key ID but different data keys
		partSize := 5 * 1024
		var sseKeys []*SSEKMSKey

		for i := 0; i < len(testData); i += partSize {
			end := i + partSize
			if end > len(testData) {
				end = len(testData)
			}

			partData := testData[i:end]

			_, sseKey, err := CreateSSEKMSEncryptedReader(strings.NewReader(partData), kmsKey.KeyID, encryptionContext)
			if err != nil {
				t.Fatalf("Failed to create encrypted reader: %v", err)
			}

			sseKeys = append(sseKeys, sseKey)
		}

		// Verify all parts use the same KMS key ID
		for i, sseKey := range sseKeys {
			if sseKey.KeyID != kmsKey.KeyID {
				t.Errorf("Part %d has wrong KMS key ID: expected %s, got %s", i, kmsKey.KeyID, sseKey.KeyID)
			}
		}

		// Verify each part has different encrypted data keys (they should be unique)
		for i := 0; i < len(sseKeys); i++ {
			for j := i + 1; j < len(sseKeys); j++ {
				if bytes.Equal(sseKeys[i].EncryptedDataKey, sseKeys[j].EncryptedDataKey) {
					t.Errorf("Parts %d and %d have identical encrypted data keys (should be unique)", i, j)
				}
			}
		}
	})
}

// TestMultipartSSEMixedScenarios tests edge cases with multipart and SSE
func TestMultipartSSEMixedScenarios(t *testing.T) {
	t.Run("Empty parts handling", func(t *testing.T) {
		keyPair := GenerateTestSSECKey(1)
		customerKey := &SSECustomerKey{
			Algorithm: "AES256",
			Key:       keyPair.Key,
			KeyMD5:    keyPair.KeyMD5,
		}

		// Test empty part
		encryptedReader, iv, err := CreateSSECEncryptedReader(strings.NewReader(""), customerKey)
		if err != nil {
			t.Fatalf("Failed to create encrypted reader for empty data: %v", err)
		}

		encryptedData, err := io.ReadAll(encryptedReader)
		if err != nil {
			t.Fatalf("Failed to read encrypted empty data: %v", err)
		}

		// Empty part should produce empty encrypted data, but still have a valid IV
		if len(encryptedData) != 0 {
			t.Errorf("Expected empty encrypted data for empty part, got %d bytes", len(encryptedData))
		}
		if len(iv) != s3_constants.AESBlockSize {
			t.Errorf("Expected IV of size %d, got %d", s3_constants.AESBlockSize, len(iv))
		}

		// Decrypt and verify
		decryptedReader, err := CreateSSECDecryptedReader(bytes.NewReader(encryptedData), customerKey, iv)
		if err != nil {
			t.Fatalf("Failed to create decrypted reader for empty data: %v", err)
		}

		decryptedData, err := io.ReadAll(decryptedReader)
		if err != nil {
			t.Fatalf("Failed to read decrypted empty data: %v", err)
		}

		if len(decryptedData) != 0 {
			t.Errorf("Expected empty decrypted data, got %d bytes", len(decryptedData))
		}
	})

	t.Run("Single byte parts", func(t *testing.T) {
		keyPair := GenerateTestSSECKey(1)
		customerKey := &SSECustomerKey{
			Algorithm: "AES256",
			Key:       keyPair.Key,
			KeyMD5:    keyPair.KeyMD5,
		}

		testData := "ABCDEFGHIJ"
		var encryptedParts [][]byte
		var partIVs [][]byte

		// Encrypt each byte as a separate part
		for i, b := range []byte(testData) {
			partData := string(b)

			encryptedReader, iv, err := CreateSSECEncryptedReader(strings.NewReader(partData), customerKey)
			if err != nil {
				t.Fatalf("Failed to create encrypted reader for byte %d: %v", i, err)
			}

			encryptedPart, err := io.ReadAll(encryptedReader)
			if err != nil {
				t.Fatalf("Failed to read encrypted byte %d: %v", i, err)
			}

			encryptedParts = append(encryptedParts, encryptedPart)
			partIVs = append(partIVs, iv)
		}

		// Reconstruct
		var reconstructedData strings.Builder

		for i, encryptedPart := range encryptedParts {
			decryptedReader, err := CreateSSECDecryptedReader(bytes.NewReader(encryptedPart), customerKey, partIVs[i])
			if err != nil {
				t.Fatalf("Failed to create decrypted reader for byte %d: %v", i, err)
			}

			decryptedPart, err := io.ReadAll(decryptedReader)
			if err != nil {
				t.Fatalf("Failed to read decrypted byte %d: %v", i, err)
			}

			reconstructedData.Write(decryptedPart)
		}

		if reconstructedData.String() != testData {
			t.Errorf("Expected %s, got %s", testData, reconstructedData.String())
		}
	})

	t.Run("Very large parts", func(t *testing.T) {
		keyPair := GenerateTestSSECKey(1)
		customerKey := &SSECustomerKey{
			Algorithm: "AES256",
			Key:       keyPair.Key,
			KeyMD5:    keyPair.KeyMD5,
		}

		// Create a large part (1MB)
		largeData := make([]byte, 1024*1024)
		for i := range largeData {
			largeData[i] = byte(i % 256)
		}

		// Encrypt
		encryptedReader, iv, err := CreateSSECEncryptedReader(bytes.NewReader(largeData), customerKey)
		if err != nil {
			t.Fatalf("Failed to create encrypted reader for large data: %v", err)
		}

		encryptedData, err := io.ReadAll(encryptedReader)
		if err != nil {
			t.Fatalf("Failed to read encrypted large data: %v", err)
		}

		// Decrypt
		decryptedReader, err := CreateSSECDecryptedReader(bytes.NewReader(encryptedData), customerKey, iv)
		if err != nil {
			t.Fatalf("Failed to create decrypted reader for large data: %v", err)
		}

		decryptedData, err := io.ReadAll(decryptedReader)
		if err != nil {
			t.Fatalf("Failed to read decrypted large data: %v", err)
		}

		if !bytes.Equal(decryptedData, largeData) {
			t.Error("Large data doesn't match after encryption/decryption")
		}
	})
}

// TestMultipartSSEPerformance tests performance characteristics of SSE with multipart
func TestMultipartSSEPerformance(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping performance test in short mode")
	}

	t.Run("SSE-C performance with multiple parts", func(t *testing.T) {
		keyPair := GenerateTestSSECKey(1)
		customerKey := &SSECustomerKey{
			Algorithm: "AES256",
			Key:       keyPair.Key,
			KeyMD5:    keyPair.KeyMD5,
		}

		partSize := 64 * 1024 // 64KB parts
		numParts := 10

		for partNum := 0; partNum < numParts; partNum++ {
			partData := make([]byte, partSize)
			for i := range partData {
				partData[i] = byte((partNum + i) % 256)
			}

			// Encrypt
			encryptedReader, iv, err := CreateSSECEncryptedReader(bytes.NewReader(partData), customerKey)
			if err != nil {
				t.Fatalf("Failed to create encrypted reader for part %d: %v", partNum, err)
			}

			encryptedData, err := io.ReadAll(encryptedReader)
			if err != nil {
				t.Fatalf("Failed to read encrypted data for part %d: %v", partNum, err)
			}

			// Decrypt
			decryptedReader, err := CreateSSECDecryptedReader(bytes.NewReader(encryptedData), customerKey, iv)
			if err != nil {
				t.Fatalf("Failed to create decrypted reader for part %d: %v", partNum, err)
			}

			decryptedData, err := io.ReadAll(decryptedReader)
			if err != nil {
				t.Fatalf("Failed to read decrypted data for part %d: %v", partNum, err)
			}

			if !bytes.Equal(decryptedData, partData) {
				t.Errorf("Data mismatch for part %d", partNum)
			}
		}
	})

	t.Run("SSE-KMS performance with multiple parts", func(t *testing.T) {
		kmsKey := SetupTestKMS(t)
		defer kmsKey.Cleanup()

		partSize := 64 * 1024 // 64KB parts
		numParts := 5         // Fewer parts for KMS due to overhead
		encryptionContext := BuildEncryptionContext("test-bucket", "test-object", false)

		for partNum := 0; partNum < numParts; partNum++ {
			partData := make([]byte, partSize)
			for i := range partData {
				partData[i] = byte((partNum + i) % 256)
			}

			// Encrypt
			encryptedReader, sseKey, err := CreateSSEKMSEncryptedReader(bytes.NewReader(partData), kmsKey.KeyID, encryptionContext)
			if err != nil {
				t.Fatalf("Failed to create encrypted reader for part %d: %v", partNum, err)
			}

			encryptedData, err := io.ReadAll(encryptedReader)
			if err != nil {
				t.Fatalf("Failed to read encrypted data for part %d: %v", partNum, err)
			}

			// Decrypt
			decryptedReader, err := CreateSSEKMSDecryptedReader(bytes.NewReader(encryptedData), sseKey)
			if err != nil {
				t.Fatalf("Failed to create decrypted reader for part %d: %v", partNum, err)
			}

			decryptedData, err := io.ReadAll(decryptedReader)
			if err != nil {
				t.Fatalf("Failed to read decrypted data for part %d: %v", partNum, err)
			}

			if !bytes.Equal(decryptedData, partData) {
				t.Errorf("Data mismatch for part %d", partNum)
			}
		}
	})
}
