package integration_test

import (
	"crypto/ecdsa"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"testing"

	"github.com/rwadurian/mpc-system/pkg/tss"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

// TestFull2of3MPCFlow tests the complete 2-of-3 MPC flow:
// 1. Key generation with 3 parties
// 2. Signing with 2 parties (threshold)
// 3. Signature verification
func TestFull2of3MPCFlow(t *testing.T) {
	fmt.Println("========================================")
	fmt.Println("  MPC 2-of-3 Full Flow Integration Test")
	fmt.Println("========================================")

	// ============================================
	// Step 1: Key Generation (2-of-3)
	// ============================================
	fmt.Println("\n[Step 1] Running 2-of-3 Distributed Key Generation...")
	fmt.Println("  - Threshold (t): 1 (meaning t+1=2 signers required)")
	fmt.Println("  - Total Parties (n): 3")

	// In tss-lib, threshold=1 means 2 signers are required (t+1)
	threshold := 1
	totalParties := 3

	keygenResults, err := tss.RunLocalKeygen(threshold, totalParties)
	require.NoError(t, err, "Keygen should succeed")
	require.Len(t, keygenResults, 3, "Should have 3 key shares")

	// Extract the shared public key
	publicKey := keygenResults[0].PublicKey
	require.NotNil(t, publicKey, "Public key should not be nil")

	fmt.Printf("  [OK] Key generation completed!\n")
	fmt.Printf("      Public Key X: %s...\n", publicKey.X.Text(16)[:32])
	fmt.Printf("      Public Key Y: %s...\n", publicKey.Y.Text(16)[:32])

	// Verify all parties have the same public key
	for i, result := range keygenResults {
		assert.Equal(t, publicKey.X, result.PublicKey.X, "Party %d should have same X", i)
		assert.Equal(t, publicKey.Y, result.PublicKey.Y, "Party %d should have same Y", i)
	}
	fmt.Println("      All parties have consistent public key")

	// ============================================
	// Step 2: Signing with 2 Parties (Threshold)
	// ============================================
	fmt.Println("\n[Step 2] Running Threshold Signing (2-of-3)...")

	// Create a message to sign
	message := []byte("Hello MPC World! This is a test transaction.")
	messageHash := sha256.Sum256(message)

	fmt.Printf("      Message: \"%s\"\n", string(message))
	fmt.Printf("      Message Hash: %s\n", hex.EncodeToString(messageHash[:]))

	// Test all 3 combinations of 2 parties
	combinations := []struct {
		name    string
		parties []*tss.LocalKeygenResult
	}{
		{"Party 0 + Party 1", []*tss.LocalKeygenResult{keygenResults[0], keygenResults[1]}},
		{"Party 0 + Party 2", []*tss.LocalKeygenResult{keygenResults[0], keygenResults[2]}},
		{"Party 1 + Party 2", []*tss.LocalKeygenResult{keygenResults[1], keygenResults[2]}},
	}

	for i, combo := range combinations {
		fmt.Printf("\n  [Signing %d] %s\n", i+1, combo.name)

		signResult, err := tss.RunLocalSigning(threshold, combo.parties, messageHash[:])
		require.NoError(t, err, "Signing with %s should succeed", combo.name)

		// Verify signature components
		require.NotNil(t, signResult.R, "R should not be nil")
		require.NotNil(t, signResult.S, "S should not be nil")
		require.Len(t, signResult.Signature, 64, "Signature should be 64 bytes")

		fmt.Printf("      R: %s...\n", signResult.R.Text(16)[:32])
		fmt.Printf("      S: %s...\n", signResult.S.Text(16)[:32])
		fmt.Printf("      Recovery ID: %d\n", signResult.RecoveryID)

		// ============================================
		// Step 3: Verify Signature
		// ============================================
		valid := ecdsa.Verify(publicKey, messageHash[:], signResult.R, signResult.S)
		require.True(t, valid, "Signature verification should pass for %s", combo.name)

		fmt.Printf("      [OK] Signature verified successfully!\n")
	}

	// ============================================
	// Step 4: Test Different Messages
	// ============================================
	fmt.Println("\n[Step 3] Testing with Different Messages...")

	messages := []string{
		"Transaction: Send 1.5 ETH to 0x1234...",
		"Contract call: approve(spender, amount)",
		"NFT transfer: tokenId=42",
	}

	signers := []*tss.LocalKeygenResult{keygenResults[0], keygenResults[1]}

	for _, msg := range messages {
		msgHash := sha256.Sum256([]byte(msg))
		signResult, err := tss.RunLocalSigning(threshold, signers, msgHash[:])
		require.NoError(t, err)

		valid := ecdsa.Verify(publicKey, msgHash[:], signResult.R, signResult.S)
		require.True(t, valid)

		fmt.Printf("      [OK] Message: \"%s...\"\n", msg[:min(30, len(msg))])
	}

	// ============================================
	// Summary
	// ============================================
	fmt.Println("\n========================================")
	fmt.Println("  Test Summary")
	fmt.Println("========================================")
	fmt.Println("  [OK] 2-of-3 Key Generation: PASSED")
	fmt.Println("  [OK] Threshold Signing (3 combinations): PASSED")
	fmt.Println("  [OK] Signature Verification: PASSED")
	fmt.Println("  [OK] Multi-message Signing: PASSED")
	fmt.Println("========================================")
	fmt.Println("  All MPC operations completed successfully!")
	fmt.Println("========================================")
}

// TestSecurityProperties tests security properties of the MPC system
func TestSecurityProperties(t *testing.T) {
	fmt.Println("\n========================================")
	fmt.Println("  Security Properties Test")
	fmt.Println("========================================")

	threshold := 1
	totalParties := 3

	// Generate keys
	keygenResults, err := tss.RunLocalKeygen(threshold, totalParties)
	require.NoError(t, err)

	publicKey := keygenResults[0].PublicKey
	message := []byte("Security test message")
	messageHash := sha256.Sum256(message)

	// Test 1: Single party cannot sign
	fmt.Println("\n[Test 1] Verifying single party cannot sign alone...")
	// Note: With threshold=1, minimum 2 parties are required
	// Attempting to sign with 1 party should fail
	singleParty := []*tss.LocalKeygenResult{keygenResults[0]}
	_, err = tss.RunLocalSigning(threshold, singleParty, messageHash[:])
	// This should fail because we need at least t+1=2 parties
	if err != nil {
		fmt.Println("      [OK] Single party signing correctly rejected")
	} else {
		t.Error("Single party should not be able to sign")
	}

	// Test 2: Different key shares produce same public key
	fmt.Println("\n[Test 2] Verifying key share consistency...")
	for i := 0; i < totalParties; i++ {
		assert.Equal(t, publicKey.X.Cmp(keygenResults[i].PublicKey.X), 0)
		assert.Equal(t, publicKey.Y.Cmp(keygenResults[i].PublicKey.Y), 0)
	}
	fmt.Println("      [OK] All parties have consistent public key")

	// Test 3: Signatures from different party combinations verify with same public key
	fmt.Println("\n[Test 3] Verifying signature consistency across party combinations...")
	combo1 := []*tss.LocalKeygenResult{keygenResults[0], keygenResults[1]}
	combo2 := []*tss.LocalKeygenResult{keygenResults[1], keygenResults[2]}

	sig1, err := tss.RunLocalSigning(threshold, combo1, messageHash[:])
	require.NoError(t, err)

	sig2, err := tss.RunLocalSigning(threshold, combo2, messageHash[:])
	require.NoError(t, err)

	// Both signatures should verify with the same public key
	valid1 := ecdsa.Verify(publicKey, messageHash[:], sig1.R, sig1.S)
	valid2 := ecdsa.Verify(publicKey, messageHash[:], sig2.R, sig2.S)

	assert.True(t, valid1, "Signature from combo1 should verify")
	assert.True(t, valid2, "Signature from combo2 should verify")
	fmt.Println("      [OK] All party combinations produce valid signatures")

	fmt.Println("\n========================================")
	fmt.Println("  Security tests passed!")
	fmt.Println("========================================")
}

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}
