package p256k1

import (
	"testing"
)

func TestSchnorrSignVerify(t *testing.T) {
	// Generate keypair
	kp, err := KeyPairGenerate()
	if err != nil {
		t.Fatalf("failed to generate keypair: %v", err)
	}
	defer kp.Clear()

	// Get x-only pubkey
	xonly, err := kp.XOnlyPubkey()
	if err != nil {
		t.Fatalf("failed to get x-only pubkey: %v", err)
	}

	// Create message
	msg := make([]byte, 32)
	for i := range msg {
		msg[i] = byte(i)
	}

	// Sign
	var sig [64]byte
	if err := SchnorrSign(sig[:], msg, kp, nil); err != nil {
		t.Fatalf("failed to sign: %v", err)
	}

	// Verify
	if !SchnorrVerify(sig[:], msg, xonly) {
		t.Error("signature verification failed")
	}

	// Test with wrong message
	wrongMsg := make([]byte, 32)
	copy(wrongMsg, msg)
	wrongMsg[0] ^= 1
	if SchnorrVerify(sig[:], wrongMsg, xonly) {
		t.Error("signature verification should fail with wrong message")
	}
}

func TestSchnorrSignWithAuxRand(t *testing.T) {
	// Generate keypair
	kp, err := KeyPairGenerate()
	if err != nil {
		t.Fatalf("failed to generate keypair: %v", err)
	}
	defer kp.Clear()

	// Get x-only pubkey
	xonly, err := kp.XOnlyPubkey()
	if err != nil {
		t.Fatalf("failed to get x-only pubkey: %v", err)
	}

	// Create message
	msg := make([]byte, 32)
	for i := range msg {
		msg[i] = byte(i)
	}

	// Auxiliary randomness
	auxRand := make([]byte, 32)
	for i := range auxRand {
		auxRand[i] = byte(i + 100)
	}

	// Sign
	var sig [64]byte
	if err := SchnorrSign(sig[:], msg, kp, auxRand); err != nil {
		t.Fatalf("failed to sign: %v", err)
	}

	// Verify
	if !SchnorrVerify(sig[:], msg, xonly) {
		t.Error("signature verification failed")
	}
}

func TestSchnorrVerifyInvalid(t *testing.T) {
	// Generate keypair
	kp, err := KeyPairGenerate()
	if err != nil {
		t.Fatalf("failed to generate keypair: %v", err)
	}
	defer kp.Clear()

	// Get x-only pubkey
	xonly, err := kp.XOnlyPubkey()
	if err != nil {
		t.Fatalf("failed to get x-only pubkey: %v", err)
	}

	msg := make([]byte, 32)

	// Test with invalid signature length
	if SchnorrVerify([]byte{1}, msg, xonly) {
		t.Error("should fail with invalid signature length")
	}

	// Test with invalid message length
	var sig [64]byte
	if SchnorrVerify(sig[:], []byte{1}, xonly) {
		t.Error("should fail with invalid message length")
	}

	// Test with nil pubkey
	if SchnorrVerify(sig[:], msg, nil) {
		t.Error("should fail with nil pubkey")
	}
}

func TestNonceFunctionBIP340(t *testing.T) {
	key32 := make([]byte, 32)
	xonlyPk32 := make([]byte, 32)
	msg := []byte("test message")
	auxRand32 := make([]byte, 32)

	// Initialize test data
	for i := range key32 {
		key32[i] = byte(i)
	}
	for i := range xonlyPk32 {
		xonlyPk32[i] = byte(i + 10)
	}
	for i := range auxRand32 {
		auxRand32[i] = byte(i + 20)
	}

	// Test with aux random
	var nonce1 [32]byte
	if err := NonceFunctionBIP340(nonce1[:], msg, key32, xonlyPk32, auxRand32); err != nil {
		t.Fatalf("nonce generation failed: %v", err)
	}

	// Test without aux random
	var nonce2 [32]byte
	if err := NonceFunctionBIP340(nonce2[:], msg, key32, xonlyPk32, nil); err != nil {
		t.Fatalf("nonce generation failed: %v", err)
	}

	// Nonces should be different
	allSame := true
	for i := 0; i < 32; i++ {
		if nonce1[i] != nonce2[i] {
			allSame = false
			break
		}
	}
	if allSame {
		t.Error("nonces should differ with different aux random")
	}
}

func TestSchnorrMultipleSignatures(t *testing.T) {
	// Test that multiple signatures with same keypair are different when using different aux_rand
	kp, err := KeyPairGenerate()
	if err != nil {
		t.Fatalf("failed to generate keypair: %v", err)
	}
	defer kp.Clear()

	xonly, err := kp.XOnlyPubkey()
	if err != nil {
		t.Fatalf("failed to get x-only pubkey: %v", err)
	}

	msg := make([]byte, 32)

	// Sign without aux_rand (deterministic - should be same)
	var sig1, sig2 [64]byte
	if err := SchnorrSign(sig1[:], msg, kp, nil); err != nil {
		t.Fatalf("failed to sign: %v", err)
	}
	if err := SchnorrSign(sig2[:], msg, kp, nil); err != nil {
		t.Fatalf("failed to sign: %v", err)
	}

	// Both should verify
	if !SchnorrVerify(sig1[:], msg, xonly) {
		t.Error("signature 1 verification failed")
	}
	if !SchnorrVerify(sig2[:], msg, xonly) {
		t.Error("signature 2 verification failed")
	}

	// Without aux_rand, signatures should be deterministic (same)
	allSame := true
	for i := 0; i < 64; i++ {
		if sig1[i] != sig2[i] {
			allSame = false
			break
		}
	}
	if !allSame {
		t.Error("without aux_rand, signatures should be deterministic (same)")
	}

	// Sign with different aux_rand (should be different)
	auxRand1 := make([]byte, 32)
	auxRand2 := make([]byte, 32)
	for i := range auxRand1 {
		auxRand1[i] = byte(i)
		auxRand2[i] = byte(i + 1)
	}

	if err := SchnorrSign(sig1[:], msg, kp, auxRand1); err != nil {
		t.Fatalf("failed to sign: %v", err)
	}
	if err := SchnorrSign(sig2[:], msg, kp, auxRand2); err != nil {
		t.Fatalf("failed to sign: %v", err)
	}

	// Both should verify
	if !SchnorrVerify(sig1[:], msg, xonly) {
		t.Error("signature 1 verification failed")
	}
	if !SchnorrVerify(sig2[:], msg, xonly) {
		t.Error("signature 2 verification failed")
	}

	// With different aux_rand, signatures should differ
	allSame = true
	for i := 0; i < 64; i++ {
		if sig1[i] != sig2[i] {
			allSame = false
			break
		}
	}
	if allSame {
		t.Error("with different aux_rand, signatures should differ")
	}
}

func BenchmarkSchnorrVerify(b *testing.B) {
	// Generate test data once outside the benchmark loop
	kp, err := KeyPairGenerate()
	if err != nil {
		b.Fatalf("failed to generate keypair: %v", err)
	}
	defer kp.Clear()

	xonly, err := kp.XOnlyPubkey()
	if err != nil {
		b.Fatalf("failed to get x-only pubkey: %v", err)
	}

	msg := make([]byte, 32)
	for i := range msg {
		msg[i] = byte(i)
	}

	sig := make([]byte, 64)
	if err := SchnorrSign(sig, msg, kp, nil); err != nil {
		b.Fatalf("failed to sign: %v", err)
	}

	// Convert to internal types once
	var secpXonly secp256k1_xonly_pubkey
	copy(secpXonly.data[:], xonly.data[:])

	// Benchmark verification with pre-computed values
	b.ResetTimer()
	b.ReportAllocs()

	ctx := getSchnorrVerifyContext()
	for i := 0; i < b.N; i++ {
		result := secp256k1_schnorrsig_verify(ctx, sig, msg, 32, &secpXonly)
		if result == 0 {
			b.Fatal("verification failed")
		}
	}
}
