package crypto_test

import (
	"encoding/base64"
	"strings"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"github.com/veylant/ia-gateway/internal/crypto"
)

// validKey returns a base64-encoded 32-byte key for tests.
func validKey() string {
	return base64.StdEncoding.EncodeToString([]byte("01234567890123456789012345678901"))
}

func newEncryptor(t *testing.T) *crypto.Encryptor {
	t.Helper()
	enc, err := crypto.NewEncryptor(validKey())
	require.NoError(t, err)
	return enc
}

func TestAES_Roundtrip(t *testing.T) {
	enc := newEncryptor(t)
	plaintext := "Mon numéro de sécu est 1 85 06 75 116 097 42"

	ciphertext, err := enc.Encrypt(plaintext)
	require.NoError(t, err)
	assert.NotEmpty(t, ciphertext)
	assert.NotEqual(t, plaintext, ciphertext)

	decrypted, err := enc.Decrypt(ciphertext)
	require.NoError(t, err)
	assert.Equal(t, plaintext, decrypted)
}

func TestAES_NonceUnique(t *testing.T) {
	enc := newEncryptor(t)
	plaintext := "same plaintext"

	ct1, err := enc.Encrypt(plaintext)
	require.NoError(t, err)
	ct2, err := enc.Encrypt(plaintext)
	require.NoError(t, err)

	// Two encryptions of the same plaintext must produce different ciphertexts
	// because nonces are random.
	assert.NotEqual(t, ct1, ct2)
}

func TestAES_EmptyPlaintext(t *testing.T) {
	enc := newEncryptor(t)

	ciphertext, err := enc.Encrypt("")
	require.NoError(t, err)

	decrypted, err := enc.Decrypt(ciphertext)
	require.NoError(t, err)
	assert.Equal(t, "", decrypted)
}

func TestAES_InvalidKey(t *testing.T) {
	// 16-byte key (too short for AES-256)
	shortKey := base64.StdEncoding.EncodeToString([]byte("0123456789abcdef"))
	_, err := crypto.NewEncryptor(shortKey)
	assert.Error(t, err)
	assert.True(t, strings.Contains(err.Error(), "32 bytes"))
}

func TestAES_DecryptTampered(t *testing.T) {
	enc := newEncryptor(t)

	ct, err := enc.Encrypt("some sensitive data")
	require.NoError(t, err)

	// Corrupt the last character of the base64 ciphertext.
	runes := []rune(ct)
	runes[len(runes)-1] = 'X'
	if runes[len(runes)-1] == []rune(ct)[len(runes)-1] {
		runes[len(runes)-1] = 'Y'
	}
	tampered := string(runes)

	_, err = enc.Decrypt(tampered)
	assert.Error(t, err, "decrypting tampered ciphertext should fail")
}
