package cybersecurity

import (
	"bytes"
	"testing"
)

// --- GenerateIdentity ---

func TestGenerateIdentity(t *testing.T) {
	t.Run("genera keypairs con longitudes correctas", func(t *testing.T) {
		id, err := GenerateIdentity()
		if err != nil {
			t.Fatalf("GenerateIdentity() error = %v", err)
		}
		if len(id.SignPub) != 32 {
			t.Errorf("SignPub len = %d, want 32", len(id.SignPub))
		}
		if len(id.SignPriv) != 64 {
			t.Errorf("SignPriv len = %d, want 64", len(id.SignPriv))
		}
		if len(id.KexPub) != 32 {
			t.Errorf("KexPub len = %d, want 32", len(id.KexPub))
		}
		if len(id.KexPriv) != 32 {
			t.Errorf("KexPriv len = %d, want 32", len(id.KexPriv))
		}
	})

	t.Run("dos llamadas producen identidades distintas", func(t *testing.T) {
		id1, err1 := GenerateIdentity()
		id2, err2 := GenerateIdentity()
		if err1 != nil || err2 != nil {
			t.Fatal("GenerateIdentity() error inesperado")
		}
		if bytes.Equal(id1.SignPub, id2.SignPub) {
			t.Error("SignPub idénticos en dos identidades distintas")
		}
		if bytes.Equal(id1.KexPub, id2.KexPub) {
			t.Error("KexPub idénticos en dos identidades distintas")
		}
	})
}

// --- SealAEAD / OpenAEAD ---

func TestSealOpenAEAD(t *testing.T) {
	key := make([]byte, 32)
	for i := range key {
		key[i] = byte(i + 1)
	}
	plaintext := []byte("mensaje secreto del bus de mensajería")
	aad := []byte("room:sala-general")

	t.Run("round-trip con aad", func(t *testing.T) {
		nonce, ct, err := SealAEAD(key, plaintext, aad)
		if err != nil {
			t.Fatalf("SealAEAD error = %v", err)
		}
		got, err := OpenAEAD(key, nonce, ct, aad)
		if err != nil {
			t.Fatalf("OpenAEAD error = %v", err)
		}
		if !bytes.Equal(got, plaintext) {
			t.Errorf("got %q, want %q", got, plaintext)
		}
	})

	t.Run("round-trip sin aad (nil)", func(t *testing.T) {
		nonce, ct, err := SealAEAD(key, plaintext, nil)
		if err != nil {
			t.Fatalf("SealAEAD error = %v", err)
		}
		got, err := OpenAEAD(key, nonce, ct, nil)
		if err != nil {
			t.Fatalf("OpenAEAD error = %v", err)
		}
		if !bytes.Equal(got, plaintext) {
			t.Errorf("got %q, want %q", got, plaintext)
		}
	})

	t.Run("error con clave de longitud incorrecta", func(t *testing.T) {
		_, _, err := SealAEAD(key[:16], plaintext, nil)
		if err == nil {
			t.Error("esperaba error con clave de 16 bytes, got nil")
		}
	})

	t.Run("error de autenticacion con ciphertext modificado", func(t *testing.T) {
		nonce, ct, err := SealAEAD(key, plaintext, aad)
		if err != nil {
			t.Fatalf("SealAEAD error = %v", err)
		}
		ct[0] ^= 0xFF // corromper el primer byte
		_, err = OpenAEAD(key, nonce, ct, aad)
		if err == nil {
			t.Error("esperaba error de autenticación con ciphertext corrupto, got nil")
		}
	})

	t.Run("error de autenticacion con aad distinto", func(t *testing.T) {
		nonce, ct, err := SealAEAD(key, plaintext, aad)
		if err != nil {
			t.Fatalf("SealAEAD error = %v", err)
		}
		_, err = OpenAEAD(key, nonce, ct, []byte("room:otra-sala"))
		if err == nil {
			t.Error("esperaba error de autenticación con aad distinto, got nil")
		}
	})

	t.Run("nonces distintos en llamadas sucesivas", func(t *testing.T) {
		n1, _, err1 := SealAEAD(key, plaintext, nil)
		n2, _, err2 := SealAEAD(key, plaintext, nil)
		if err1 != nil || err2 != nil {
			t.Fatal("SealAEAD error inesperado")
		}
		if bytes.Equal(n1, n2) {
			t.Error("nonces iguales en dos llamadas sucesivas (no aleatorios)")
		}
	})
}

// --- SealKeyBox / OpenKeyBox ---

func TestSealOpenKeyBox(t *testing.T) {
	t.Run("round-trip con identidad generada", func(t *testing.T) {
		id, err := GenerateIdentity()
		if err != nil {
			t.Fatalf("GenerateIdentity error = %v", err)
		}
		roomKey := make([]byte, 32)
		for i := range roomKey {
			roomKey[i] = byte(i + 42)
		}

		sealed, err := SealKeyBox(id.KexPub, roomKey)
		if err != nil {
			t.Fatalf("SealKeyBox error = %v", err)
		}

		opened, err := OpenKeyBox(id.KexPub, id.KexPriv, sealed)
		if err != nil {
			t.Fatalf("OpenKeyBox error = %v", err)
		}
		if !bytes.Equal(opened, roomKey) {
			t.Errorf("got %x, want %x", opened, roomKey)
		}
	})

	t.Run("error con recipientKexPub de longitud incorrecta", func(t *testing.T) {
		_, err := SealKeyBox(make([]byte, 16), []byte("secret"))
		if err == nil {
			t.Error("esperaba error con kexPub de 16 bytes, got nil")
		}
	})

	t.Run("error al abrir con clave equivocada", func(t *testing.T) {
		id, _ := GenerateIdentity()
		other, _ := GenerateIdentity()
		sealed, err := SealKeyBox(id.KexPub, []byte("roomkey"))
		if err != nil {
			t.Fatalf("SealKeyBox error = %v", err)
		}
		_, err = OpenKeyBox(other.KexPub, other.KexPriv, sealed)
		if err == nil {
			t.Error("esperaba error al abrir con keypair distinto, got nil")
		}
	})

	t.Run("error con mensaje truncado", func(t *testing.T) {
		id, _ := GenerateIdentity()
		_, err := OpenKeyBox(id.KexPub, id.KexPriv, []byte("corto"))
		if err == nil {
			t.Error("esperaba error con sealedMsg truncado, got nil")
		}
	})
}

// --- SignEd25519 / VerifyEd25519 ---

func TestSignVerifyEd25519(t *testing.T) {
	t.Run("firma y verificacion exitosa", func(t *testing.T) {
		id, err := GenerateIdentity()
		if err != nil {
			t.Fatalf("GenerateIdentity error = %v", err)
		}
		msg := []byte("evento:room_key_rotation:v2")
		sig := SignEd25519(id.SignPriv, msg)
		if len(sig) != 64 {
			t.Errorf("sig len = %d, want 64", len(sig))
		}
		if !VerifyEd25519(id.SignPub, msg, sig) {
			t.Error("VerifyEd25519 devolvió false para firma válida")
		}
	})

	t.Run("firma es determinista (misma entrada, misma firma)", func(t *testing.T) {
		id, _ := GenerateIdentity()
		msg := []byte("determinismo criptografico")
		sig1 := SignEd25519(id.SignPriv, msg)
		sig2 := SignEd25519(id.SignPriv, msg)
		if !bytes.Equal(sig1, sig2) {
			t.Error("Ed25519 debe ser determinista: mismas entradas deben producir misma firma")
		}
	})

	t.Run("falla con mensaje modificado", func(t *testing.T) {
		id, _ := GenerateIdentity()
		msg := []byte("mensaje original")
		sig := SignEd25519(id.SignPriv, msg)
		modified := []byte("mensaje modificado")
		if VerifyEd25519(id.SignPub, modified, sig) {
			t.Error("VerifyEd25519 devolvió true para mensaje modificado")
		}
	})

	t.Run("falla con clave publica incorrecta", func(t *testing.T) {
		id1, _ := GenerateIdentity()
		id2, _ := GenerateIdentity()
		msg := []byte("autenticidad del remitente")
		sig := SignEd25519(id1.SignPriv, msg)
		if VerifyEd25519(id2.SignPub, msg, sig) {
			t.Error("VerifyEd25519 devolvió true con clave pública de otra identidad")
		}
	})

	t.Run("falla con firma corrupta", func(t *testing.T) {
		id, _ := GenerateIdentity()
		msg := []byte("integridad")
		sig := SignEd25519(id.SignPriv, msg)
		sig[0] ^= 0xFF
		if VerifyEd25519(id.SignPub, msg, sig) {
			t.Error("VerifyEd25519 devolvió true con firma corrupta")
		}
	})
}

// --- Integración: flujo completo megolm-reducido ---

func TestE2EMessagingFlow(t *testing.T) {
	t.Run("flujo completo: generar identidad, distribuir clave de sala, cifrar y firmar mensaje", func(t *testing.T) {
		// Servidor genera identidad
		server, err := GenerateIdentity()
		if err != nil {
			t.Fatalf("GenerateIdentity server: %v", err)
		}
		// Usuario genera identidad
		user, err := GenerateIdentity()
		if err != nil {
			t.Fatalf("GenerateIdentity user: %v", err)
		}

		// Servidor genera clave de sala y la distribuye al usuario cifrada con su KexPub
		roomKey := make([]byte, 32)
		for i := range roomKey {
			roomKey[i] = byte(i)
		}
		sealedKey, err := SealKeyBox(user.KexPub, roomKey)
		if err != nil {
			t.Fatalf("SealKeyBox: %v", err)
		}

		// Usuario desella la clave de sala
		gotRoomKey, err := OpenKeyBox(user.KexPub, user.KexPriv, sealedKey)
		if err != nil {
			t.Fatalf("OpenKeyBox: %v", err)
		}
		if !bytes.Equal(gotRoomKey, roomKey) {
			t.Fatal("clave de sala distribuida no coincide")
		}

		// Usuario cifra un mensaje con la clave de sala
		plainMsg := []byte("hola sala, este es mi primer mensaje cifrado e2e")
		aad := []byte("room:sala-secreta:seq:1")
		nonce, ct, err := SealAEAD(gotRoomKey, plainMsg, aad)
		if err != nil {
			t.Fatalf("SealAEAD: %v", err)
		}

		// Usuario firma el ciphertext para autenticación del remitente
		sig := SignEd25519(user.SignPriv, ct)

		// Receptor verifica firma del remitente
		if !VerifyEd25519(user.SignPub, ct, sig) {
			t.Fatal("verificación de firma del remitente falló")
		}

		// Receptor descifra el mensaje
		decrypted, err := OpenAEAD(gotRoomKey, nonce, ct, aad)
		if err != nil {
			t.Fatalf("OpenAEAD: %v", err)
		}
		if !bytes.Equal(decrypted, plainMsg) {
			t.Errorf("mensaje descifrado %q != original %q", decrypted, plainMsg)
		}

		// Servidor tiene distinta identidad que el usuario (las claves no se confunden)
		if bytes.Equal(server.SignPub, user.SignPub) {
			t.Error("server y user tienen la misma clave pública de firma")
		}
	})
}
