package encryption

import (
	"bytes"
	"io"
	"testing"
)

func TestEncryptDecrypt(t *testing.T) {
	// Test data
	original := []byte("This is a secret database backup that needs encryption! [LOCK]")

	// Test with passphrase
	t.Run("Passphrase", func(t *testing.T) {
		var encrypted bytes.Buffer

		// Encrypt
		writer, err := NewEncryptionWriter(&encrypted, EncryptionOptions{
			Passphrase: "super-secret-password",
		})
		if err != nil {
			t.Fatalf("Failed to create encryption writer: %v", err)
		}

		if _, err := writer.Write(original); err != nil {
			t.Fatalf("Failed to write data: %v", err)
		}

		if err := writer.Close(); err != nil {
			t.Fatalf("Failed to close writer: %v", err)
		}

		t.Logf("Original size: %d bytes", len(original))
		t.Logf("Encrypted size: %d bytes", encrypted.Len())

		// Verify encrypted data is different from original
		if bytes.Contains(encrypted.Bytes(), original) {
			t.Error("Encrypted data contains plaintext - encryption failed!")
		}

		// Decrypt
		reader, err := NewDecryptionReader(&encrypted, EncryptionOptions{
			Passphrase: "super-secret-password",
		})
		if err != nil {
			t.Fatalf("Failed to create decryption reader: %v", err)
		}

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

		// Verify decrypted matches original
		if !bytes.Equal(decrypted, original) {
			t.Errorf("Decrypted data doesn't match original\nOriginal: %s\nDecrypted: %s",
				string(original), string(decrypted))
		}

		t.Log("[OK] Encryption/decryption successful")
	})

	// Test with direct key
	t.Run("DirectKey", func(t *testing.T) {
		key, err := GenerateKey()
		if err != nil {
			t.Fatalf("Failed to generate key: %v", err)
		}

		var encrypted bytes.Buffer

		// Encrypt
		writer, err := NewEncryptionWriter(&encrypted, EncryptionOptions{
			Key: key,
		})
		if err != nil {
			t.Fatalf("Failed to create encryption writer: %v", err)
		}

		if _, err := writer.Write(original); err != nil {
			t.Fatalf("Failed to write data: %v", err)
		}

		if err := writer.Close(); err != nil {
			t.Fatalf("Failed to close writer: %v", err)
		}

		// Decrypt
		reader, err := NewDecryptionReader(&encrypted, EncryptionOptions{
			Key: key,
		})
		if err != nil {
			t.Fatalf("Failed to create decryption reader: %v", err)
		}

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

		if !bytes.Equal(decrypted, original) {
			t.Errorf("Decrypted data doesn't match original")
		}

		t.Log("[OK] Direct key encryption/decryption successful")
	})

	// Test wrong password
	t.Run("WrongPassword", func(t *testing.T) {
		var encrypted bytes.Buffer

		// Encrypt
		writer, err := NewEncryptionWriter(&encrypted, EncryptionOptions{
			Passphrase: "correct-password",
		})
		if err != nil {
			t.Fatalf("Failed to create encryption writer: %v", err)
		}

		writer.Write(original)
		writer.Close()

		// Try to decrypt with wrong password
		reader, err := NewDecryptionReader(&encrypted, EncryptionOptions{
			Passphrase: "wrong-password",
		})
		if err != nil {
			t.Fatalf("Failed to create decryption reader: %v", err)
		}

		_, err = io.ReadAll(reader)
		if err == nil {
			t.Error("Expected decryption to fail with wrong password, but it succeeded")
		}

		t.Logf("[OK] Wrong password correctly rejected: %v", err)
	})
}

func TestLargeData(t *testing.T) {
	// Test with large data (1MB) to test chunking
	original := make([]byte, 1024*1024)
	for i := range original {
		original[i] = byte(i % 256)
	}

	var encrypted bytes.Buffer

	// Encrypt
	writer, err := NewEncryptionWriter(&encrypted, EncryptionOptions{
		Passphrase: "test-password",
	})
	if err != nil {
		t.Fatalf("Failed to create encryption writer: %v", err)
	}

	if _, err := writer.Write(original); err != nil {
		t.Fatalf("Failed to write data: %v", err)
	}

	if err := writer.Close(); err != nil {
		t.Fatalf("Failed to close writer: %v", err)
	}

	t.Logf("Original size: %d bytes", len(original))
	t.Logf("Encrypted size: %d bytes", encrypted.Len())
	t.Logf("Overhead: %.2f%%", float64(encrypted.Len()-len(original))/float64(len(original))*100)

	// Decrypt
	reader, err := NewDecryptionReader(&encrypted, EncryptionOptions{
		Passphrase: "test-password",
	})
	if err != nil {
		t.Fatalf("Failed to create decryption reader: %v", err)
	}

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

	if !bytes.Equal(decrypted, original) {
		t.Errorf("Large data decryption failed")
	}

	t.Log("[OK] Large data encryption/decryption successful")
}

func TestKeyGeneration(t *testing.T) {
	// Test key generation
	key1, err := GenerateKey()
	if err != nil {
		t.Fatalf("Failed to generate key: %v", err)
	}

	if len(key1) != KeySize {
		t.Errorf("Key size mismatch: expected %d, got %d", KeySize, len(key1))
	}

	// Generate another key and verify it's different
	key2, err := GenerateKey()
	if err != nil {
		t.Fatalf("Failed to generate second key: %v", err)
	}

	if bytes.Equal(key1, key2) {
		t.Error("Generated keys are identical - randomness broken!")
	}

	t.Log("[OK] Key generation successful")
}

func TestKeyDerivation(t *testing.T) {
	passphrase := "my-secret-passphrase"
	salt1, _ := GenerateSalt()

	// Derive key twice with same salt - should be identical
	key1 := DeriveKey(passphrase, salt1)
	key2 := DeriveKey(passphrase, salt1)

	if !bytes.Equal(key1, key2) {
		t.Error("Key derivation not deterministic")
	}

	// Derive with different salt - should be different
	salt2, _ := GenerateSalt()
	key3 := DeriveKey(passphrase, salt2)

	if bytes.Equal(key1, key3) {
		t.Error("Different salts produced same key")
	}

	t.Log("[OK] Key derivation successful")
}
