package lib

import (
	"bytes"
	"strings"
	"testing"
)

func TestPackUnpack(t *testing.T) {
	key := make([]byte, 32)
	for i := range key {
		key[i] = byte(i)
	}

	tests := []struct {
		name  string
		input string
	}{
		{"simple", "hello world"},
		{"empty", ""},
		{"unicode", "こんにちは世界 🌍 مرحبا"},
		{"json", `{"key": "value", "nested": {"data": 123}}`},
		{"large", strings.Repeat("a", 1024*1024)}, // 1MB
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			packed, err := Pack(key, tc.input)
			if err != nil {
				t.Fatalf("Pack failed: %v", err)
			}

			unpacked, err := Unpack(key, packed)
			if err != nil {
				t.Fatalf("Unpack failed: %v", err)
			}

			if unpacked != tc.input {
				if len(tc.input) > 100 {
					t.Errorf("round-trip failed: lengths differ (got %d, want %d)", len(unpacked), len(tc.input))
				} else {
					t.Errorf("round-trip failed: got %q, want %q", unpacked, tc.input)
				}
			}
		})
	}
}

func TestPackUnpackEmptyInput(t *testing.T) {
	key := make([]byte, 32)
	for i := range key {
		key[i] = byte(i)
	}

	// Unpack nil/empty ciphertext should return empty
	result, err := Unpack(key, nil)
	if err != nil {
		t.Fatalf("Unpack nil failed: %v", err)
	}
	if result != "" {
		t.Errorf("expected empty for nil input, got %q", result)
	}

	result, err = Unpack(key, []byte{})
	if err != nil {
		t.Fatalf("Unpack empty bytes failed: %v", err)
	}
	if result != "" {
		t.Errorf("expected empty for empty bytes, got %q", result)
	}
}

func TestBlindIndex(t *testing.T) {
	key1 := make([]byte, 32)
	key2 := make([]byte, 32)
	for i := range key1 {
		key1[i] = byte(i)
		key2[i] = byte(i + 1) // Different key
	}

	plaintext := "searchable-term"

	// Same input + same key = same index
	index1 := BlindIndex(key1, plaintext)
	index2 := BlindIndex(key1, plaintext)
	if !bytes.Equal(index1, index2) {
		t.Error("same input + key should produce same index")
	}

	// Same input + different key = different index
	index3 := BlindIndex(key2, plaintext)
	if bytes.Equal(index1, index3) {
		t.Error("different keys should produce different indexes")
	}

	// Different input + same key = different index
	index4 := BlindIndex(key1, "different-term")
	if bytes.Equal(index1, index4) {
		t.Error("different inputs should produce different indexes")
	}

	// Index should be 32 bytes (SHA-256)
	if len(index1) != 32 {
		t.Errorf("index length should be 32, got %d", len(index1))
	}
}

func TestDeriveProjectKey(t *testing.T) {
	masterKey := make([]byte, 32)
	for i := range masterKey {
		masterKey[i] = byte(i)
	}

	// Deterministic: same master + projectID = same key
	key1, err := DeriveProjectKey(masterKey, "project-123")
	if err != nil {
		t.Fatalf("DeriveProjectKey failed: %v", err)
	}
	key2, err := DeriveProjectKey(masterKey, "project-123")
	if err != nil {
		t.Fatalf("DeriveProjectKey failed: %v", err)
	}
	if !bytes.Equal(key1, key2) {
		t.Error("same master + projectID should produce same key")
	}

	// Different projectID = different key
	key3, err := DeriveProjectKey(masterKey, "project-456")
	if err != nil {
		t.Fatalf("DeriveProjectKey failed: %v", err)
	}
	if bytes.Equal(key1, key3) {
		t.Error("different projectID should produce different key")
	}

	// Key should be 32 bytes (AES-256)
	if len(key1) != 32 {
		t.Errorf("key length should be 32, got %d", len(key1))
	}
}

func TestDeriveHMACKey(t *testing.T) {
	masterKey := make([]byte, 32)
	for i := range masterKey {
		masterKey[i] = byte(i)
	}

	// HMAC key should be different from project key for same projectID
	projectKey, _ := DeriveProjectKey(masterKey, "project-123")
	hmacKey, err := DeriveHMACKey(masterKey, "project-123")
	if err != nil {
		t.Fatalf("DeriveHMACKey failed: %v", err)
	}
	if bytes.Equal(projectKey, hmacKey) {
		t.Error("HMAC key should differ from project key")
	}

	// HMAC key should be 32 bytes
	if len(hmacKey) != 32 {
		t.Errorf("HMAC key length should be 32, got %d", len(hmacKey))
	}
}

func TestAESGCM(t *testing.T) {
	key := make([]byte, 32)
	for i := range key {
		key[i] = byte(i)
	}

	tests := []struct {
		name string
		data []byte
	}{
		{"simple", []byte("hello world")},
		{"binary", []byte{0x00, 0x01, 0x02, 0xff, 0xfe}},
		{"large", bytes.Repeat([]byte("x"), 1024*1024)}, // 1MB
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			encrypted, err := ObjectEncrypt(key, tc.data)
			if err != nil {
				t.Fatalf("ObjectEncrypt failed: %v", err)
			}

			decrypted, err := ObjectDecrypt(key, encrypted)
			if err != nil {
				t.Fatalf("ObjectDecrypt failed: %v", err)
			}

			if !bytes.Equal(decrypted, tc.data) {
				t.Errorf("round-trip failed")
			}
		})
	}
}

func TestObjectEncryptDecryptWrongKey(t *testing.T) {
	key1 := make([]byte, 32)
	key2 := make([]byte, 32)
	for i := range key1 {
		key1[i] = byte(i)
		key2[i] = byte(i + 1)
	}

	data := []byte("secret data")
	encrypted, err := ObjectEncrypt(key1, data)
	if err != nil {
		t.Fatalf("ObjectEncrypt failed: %v", err)
	}

	_, err = ObjectDecrypt(key2, encrypted)
	if err == nil {
		t.Error("decrypt with wrong key should fail")
	}
}

func TestObjectDecryptInvalidCiphertext(t *testing.T) {
	key := make([]byte, 32)

	// Too short ciphertext
	_, err := ObjectDecrypt(key, []byte{1, 2, 3})
	if err == nil {
		t.Error("decrypt too-short ciphertext should fail")
	}

	// Nil ciphertext
	_, err = ObjectDecrypt(key, nil)
	if err != ErrInvalidCiphertext {
		t.Error("decrypt nil should return ErrInvalidCiphertext")
	}
}

func TestContentHash(t *testing.T) {
	data := []byte("test data")
	hash1 := ContentHash(data)
	hash2 := ContentHash(data)

	if !bytes.Equal(hash1, hash2) {
		t.Error("same data should produce same hash")
	}

	hash3 := ContentHash([]byte("different data"))
	if bytes.Equal(hash1, hash3) {
		t.Error("different data should produce different hash")
	}

	if len(hash1) != 32 {
		t.Errorf("hash length should be 32, got %d", len(hash1))
	}
}
