package nip60

import (
	"context"
	"os"
	"testing"
	"time"

	"github.com/nbd-wtf/go-nostr"
	"github.com/nbd-wtf/go-nostr/keyer"
	"github.com/stretchr/testify/require"
)

var testRelays = []string{
	"wss://relay.damus.io",
	"wss://nos.lol",
	"wss://relay.nostr.band",
}

func TestWalletTransfer(t *testing.T) {
	ctx := context.Background()

	// setup first wallet
	sk1 := os.Getenv("NIP60_SECRET_KEY_1")
	if sk1 == "" {
		t.Skip("NIP60_SECRET_KEY_1 not set")
	}
	kr1, err := keyer.NewPlainKeySigner(sk1)
	if err != nil {
		t.Fatal(err)
	}

	pool := nostr.NewSimplePool(ctx)
	stash1 := LoadStash(ctx, kr1, pool, testRelays)
	if stash1 == nil {
		t.Fatal("failed to load stash 1")
	}
	stash1.PublishUpdate = func(event nostr.Event, deleted, received, change *Token, isHistory bool) {
		pool.PublishMany(ctx, testRelays, event)
	}

	// setup second wallet
	sk2 := os.Getenv("NIP60_SECRET_KEY_2")
	if sk2 == "" {
		t.Skip("NIP60_SECRET_KEY_2 not set")
	}
	kr2, err := keyer.NewPlainKeySigner(sk2)
	if err != nil {
		t.Fatal(err)
	}

	stash2 := LoadStash(ctx, kr2, pool, testRelays)
	if stash2 == nil {
		t.Fatal("failed to load stash 2")
	}
	stash2.PublishUpdate = func(event nostr.Event, deleted, received, change *Token, isHistory bool) {
		pool.PublishMany(ctx, testRelays, event)
	}

	// handle events from both stashes
	go func() {
		for {
			select {
			case err := <-stash1.Processed:
				if err != nil {
					t.Errorf("stash1 processing error: %v", err)
				}
			case err := <-stash2.Processed:
				if err != nil {
					t.Errorf("stash2 processing error: %v", err)
				}
			case <-ctx.Done():
				return
			}
		}
	}()

	// wait for initial load
	select {
	case <-stash1.Stable:
	case <-time.After(15 * time.Second):
		t.Fatal("timeout waiting for stash 1 to load")
	}

	select {
	case <-stash2.Stable:
	case <-time.After(15 * time.Second):
		t.Fatal("timeout waiting for stash 2 to load")
	}

	// ensure wallets exist and have tokens
	w1 := stash1.EnsureWallet("test")
	require.Greater(t, w1.Balance(), uint64(0), "wallet 1 has no balance")

	w2 := stash2.EnsureWallet("test")
	initialBalance1 := w1.Balance()
	initialBalance2 := w2.Balance()

	t.Logf("initial balances: w1=%d w2=%d", initialBalance1, initialBalance2)

	// send half of wallet 1's balance to wallet 2
	pk2, err := kr2.GetPublicKey(ctx)
	require.NoError(t, err)

	halfBalance := initialBalance1 / 2
	token, err := w1.SendToken(ctx, halfBalance, WithP2PK(pk2))
	require.NoError(t, err)

	// receive token in wallet 2
	err = w2.ReceiveToken(ctx, token)
	require.NoError(t, err)

	// verify balances
	require.Equal(t, initialBalance1-halfBalance, w1.Balance(), "wallet 1 balance wrong after send")
	require.Equal(t, initialBalance2+halfBalance, w2.Balance(), "wallet 2 balance wrong after receive")

	// now send it back
	pk1, err := kr1.GetPublicKey(ctx)
	require.NoError(t, err)

	token, err = w2.SendToken(ctx, halfBalance, WithP2PK(pk1))
	require.NoError(t, err)

	// receive token back in wallet 1
	err = w1.ReceiveToken(ctx, token)
	require.NoError(t, err)

	// verify final balances match initial
	require.Equal(t, initialBalance1, w1.Balance(), "wallet 1 final balance wrong")
	require.Equal(t, initialBalance2, w2.Balance(), "wallet 2 final balance wrong")

	t.Logf("final balances: w1=%d w2=%d", w1.Balance(), w2.Balance())
}
