package crypto

import (
	"crypto/rand"
	"encoding/base64"
	"testing"
)

func TestEncryptDecryptRoundTrip(t *testing.T) {
	plaintext := "test-secret-value"
	secretBox := newTestSecretBox(t)

	encrypted, err := secretBox.EncryptSecret(plaintext)
	if err != nil {
		t.Fatalf("EncryptSecret failed: %v", err)
	}

	if encrypted == "" {
		t.Fatal("EncryptSecret returned empty string")
	}

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

	if decrypted != plaintext {
		t.Errorf("DecryptSecret() = %q, want %q", decrypted, plaintext)
	}
}

func TestEncryptDifferentOutputs(t *testing.T) {
	plaintext := "test-secret-value"
	secretBox := newTestSecretBox(t)

	// Encrypt twice - should produce different outputs due to nonce
	encrypted1, err := secretBox.EncryptSecret(plaintext)
	if err != nil {
		t.Fatalf("EncryptSecret failed: %v", err)
	}

	encrypted2, err := secretBox.EncryptSecret(plaintext)
	if err != nil {
		t.Fatalf("EncryptSecret failed: %v", err)
	}

	if encrypted1 == encrypted2 {
		t.Error("EncryptSecret should produce different outputs for same input (nonce)")
	}

	// But both should decrypt to the same value
	decrypted1, err := secretBox.DecryptSecret(encrypted1)
	if err != nil {
		t.Fatalf("DecryptSecret failed: %v", err)
	}

	decrypted2, err := secretBox.DecryptSecret(encrypted2)
	if err != nil {
		t.Fatalf("DecryptSecret failed: %v", err)
	}

	if decrypted1 != decrypted2 {
		t.Error("Both encryptions should decrypt to the same value")
	}
}

func TestDecryptInvalidInput(t *testing.T) {
	_, err := newTestSecretBox(t).DecryptSecret("invalid-base64-!!!")
	if err == nil {
		t.Error("DecryptSecret should fail for invalid base64")
	}
}

func TestDecryptTamperedData(t *testing.T) {
	plaintext := "test-secret-value"
	secretBox := newTestSecretBox(t)
	encrypted, err := secretBox.EncryptSecret(plaintext)
	if err != nil {
		t.Fatalf("EncryptSecret failed: %v", err)
	}

	// Tamper with the ciphertext
	tampered := encrypted[:len(encrypted)-1] + "X"

	_, err = secretBox.DecryptSecret(tampered)
	if err == nil {
		t.Error("DecryptSecret should fail for tampered data")
	}
}

func newTestSecretBox(t *testing.T) *SecretBox {
	t.Helper()
	secretBox, err := NewSecretBox("")
	if err != nil {
		t.Fatalf("NewSecretBox failed: %v", err)
	}
	return secretBox
}

func TestSecretBoxesAreCryptographicallyIsolated(t *testing.T) {
	keyA := make([]byte, 32)
	keyB := make([]byte, 32)
	if _, err := rand.Read(keyA); err != nil {
		t.Fatalf("rand key A: %v", err)
	}
	if _, err := rand.Read(keyB); err != nil {
		t.Fatalf("rand key B: %v", err)
	}
	boxA, err := NewSecretBox(base64.StdEncoding.EncodeToString(keyA))
	if err != nil {
		t.Fatalf("NewSecretBox A: %v", err)
	}
	boxB, err := NewSecretBox(base64.StdEncoding.EncodeToString(keyB))
	if err != nil {
		t.Fatalf("NewSecretBox B: %v", err)
	}

	encrypted, err := boxA.EncryptSecret("tenant-a-secret")
	if err != nil {
		t.Fatalf("encrypt A: %v", err)
	}
	if _, err := boxB.DecryptSecret(encrypted); err == nil {
		t.Fatal("tenant B decrypted tenant A ciphertext")
	}
	if got, err := boxA.DecryptSecret(encrypted); err != nil || got != "tenant-a-secret" {
		t.Fatalf("tenant A decrypt = %q, %v", got, err)
	}
}
