package main

import (
	"testing"

	"btcphrasechecker/bitcoin"
	"btcphrasechecker/types"

	"github.com/tyler-smith/go-bip39"
)

const (
	testMnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
)

func TestBitcoinWalletAnalysis(t *testing.T) {
	seed := bip39.NewSeed(testMnemonic, "")

	summary, err := bitcoin.AnalyzeWallet(seed, 20, false)
	if err != nil {
		t.Fatalf("AnalyzeWallet failed: %v", err)
	}

	// Verify summary structure
	if summary == nil {
		t.Fatal("Summary is nil")
	}

	t.Logf("Total Balance: %.8f BTC", float64(summary.TotalBalance)/100000000.0)
	t.Logf("Total Received: %.8f BTC", float64(summary.TotalReceived)/100000000.0)
	t.Logf("Total Sent: %.8f BTC", float64(summary.TotalSent)/100000000.0)
	t.Logf("Total Transactions: %d", summary.TotalTxCount)
	t.Logf("Active Addresses: %d", len(summary.ActiveAddresses))

	// Verify that received - sent = balance
	expectedBalance := summary.TotalReceived - summary.TotalSent
	if summary.TotalBalance != expectedBalance {
		t.Errorf("Balance mismatch: balance=%d, received=%d, sent=%d, expected=%d",
			summary.TotalBalance, summary.TotalReceived, summary.TotalSent, expectedBalance)
	}

	// Verify active addresses
	for i, addr := range summary.ActiveAddresses {
		if addr.Address == "" {
			t.Errorf("Active address %d has empty address", i)
		}
		if addr.Path == "" {
			t.Errorf("Active address %d has empty path", i)
		}
		if addr.Chain != types.ChainBitcoin {
			t.Errorf("Active address %d has wrong chain: %s", i, addr.Chain)
		}

		// Verify address-level balance
		addrExpectedBalance := addr.Received - addr.Sent
		if addr.Balance != addrExpectedBalance {
			t.Errorf("Address %s balance mismatch: balance=%d, received=%d, sent=%d",
				addr.Address, addr.Balance, addr.Received, addr.Sent)
		}

		t.Logf("  Address %d: %s (Path: %s)", i, addr.Address, addr.Path)
		t.Logf("    Balance: %.8f BTC, Received: %.8f BTC, Sent: %.8f BTC, Txs: %d",
			float64(addr.Balance)/100000000.0,
			float64(addr.Received)/100000000.0,
			float64(addr.Sent)/100000000.0,
			addr.TxCount)
	}
}

func TestTransactionHistory(t *testing.T) {
	seed := bip39.NewSeed(testMnemonic, "")

	summary, err := bitcoin.AnalyzeWallet(seed, 20, false)
	if err != nil {
		t.Fatalf("AnalyzeWallet failed: %v", err)
	}

	if len(summary.ActiveAddresses) == 0 {
		t.Skip("No active addresses found, skipping transaction history test")
	}

	err = bitcoin.FetchTransactionHistory(summary)
	if err != nil {
		t.Fatalf("FetchTransactionHistory failed: %v", err)
	}

	t.Logf("Total transactions in history: %d", len(summary.TransactionHistory))
	t.Logf("Receive transactions: %d", summary.ReceiveTxCount)
	t.Logf("Send transactions: %d", summary.SendTxCount)

	// Verify transaction history
	var prevTime int64
	for i, tx := range summary.TransactionHistory {
		if tx.TxID == "" {
			t.Errorf("Transaction %d has empty TxID", i)
		}

		if tx.Time.IsZero() {
			t.Logf("Warning: Transaction %d has zero time (might be unconfirmed)", i)
		}

		// Verify chronological order
		currentTime := tx.Time.Unix()
		if i > 0 && currentTime < prevTime {
			t.Errorf("Transactions not in chronological order at index %d", i)
		}
		prevTime = currentTime

		// Verify transaction type
		if tx.Type != "received" && tx.Type != "sent" && tx.Type != "self" {
			t.Errorf("Transaction %d has invalid type: %s", i, tx.Type)
		}

		if i < 10 { // Log first 10 transactions
			t.Logf("  Tx %d: %s", i, tx.TxID[:16])
			t.Logf("    Date: %s", tx.Time.Format("2006-01-02 15:04:05"))
			t.Logf("    Type: %s, Amount: %.8f BTC, Balance: %.8f BTC",
				tx.Type,
				float64(tx.Amount)/100000000.0,
				float64(tx.Balance)/100000000.0)
		}
	}

	// Note: Final balance verification is complex due to self-transactions and
	// transaction ordering across multiple addresses. The running balance is calculated
	// per-address chronologically, but may not match the total wallet balance due to
	// timing and internal transfers.
	if len(summary.TransactionHistory) > 0 {
		lastTx := summary.TransactionHistory[len(summary.TransactionHistory)-1]
		t.Logf("Final tx balance: %.8f BTC, Total wallet balance: %.8f BTC",
			float64(lastTx.Balance)/100000000.0,
			float64(summary.TotalBalance)/100000000.0)
	}
}

func TestKnownAddresses(t *testing.T) {
	seed := bip39.NewSeed(testMnemonic, "")

	addresses, err := bitcoin.DeriveAddresses(seed, 20)
	if err != nil {
		t.Fatalf("DeriveAddresses failed: %v", err)
	}

	// Map of known addresses for the test mnemonic
	knownAddresses := map[string]string{
		"m/44'/0'/0'/0/0": "1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA",
		"m/44'/0'/0'/0/1": "1Ak8PffB2meyfYnbXZR9EGfLfFZVpzJvQP",
		"m/49'/0'/0'/0/0": "37VucYSaXLCAsxYyAPfbSi9eh4iEcbShgf",
		"m/84'/0'/0'/0/0": "bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu",
		"m/84'/0'/0'/0/1": "bc1qnjg0jd8228aq7egyzacy8cys3knf9xvrerkf9g",
	}

	addressMap := make(map[string]string)
	for _, addr := range addresses {
		addressMap[addr.Path] = addr.Address
	}

	for path, expectedAddr := range knownAddresses {
		actualAddr, exists := addressMap[path]
		if !exists {
			t.Errorf("Address for path %s not found", path)
			continue
		}
		if actualAddr != expectedAddr {
			t.Errorf("Address mismatch for path %s:\n  expected: %s\n  got:      %s",
				path, expectedAddr, actualAddr)
		} else {
			t.Logf("✓ %s = %s", path, actualAddr)
		}
	}
}

func TestMnemonicValidation(t *testing.T) {
	tests := []struct {
		name     string
		mnemonic string
		valid    bool
	}{
		{
			name:     "Valid 12-word mnemonic",
			mnemonic: testMnemonic,
			valid:    true,
		},
		{
			name:     "Invalid mnemonic",
			mnemonic: "invalid mnemonic phrase that should not work",
			valid:    false,
		},
		{
			name:     "Empty mnemonic",
			mnemonic: "",
			valid:    false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			valid := bip39.IsMnemonicValid(tt.mnemonic)
			if valid != tt.valid {
				t.Errorf("Expected mnemonic validity to be %v, got %v", tt.valid, valid)
			}
		})
	}
}

func TestPassphraseSupport(t *testing.T) {
	// Test that different passphrases generate different seeds
	seed1 := bip39.NewSeed(testMnemonic, "")
	seed2 := bip39.NewSeed(testMnemonic, "password123")

	if string(seed1) == string(seed2) {
		t.Error("Seeds with different passphrases should be different")
	}

	// Verify that addresses are different with different passphrases
	addresses1, err := bitcoin.DeriveAddresses(seed1, 1)
	if err != nil {
		t.Fatalf("DeriveAddresses failed for seed1: %v", err)
	}

	addresses2, err := bitcoin.DeriveAddresses(seed2, 1)
	if err != nil {
		t.Fatalf("DeriveAddresses failed for seed2: %v", err)
	}

	if len(addresses1) == 0 || len(addresses2) == 0 {
		t.Fatal("No addresses generated")
	}

	if addresses1[0].Address == addresses2[0].Address {
		t.Error("Addresses should be different with different passphrases")
	}

	t.Logf("Without passphrase: %s", addresses1[0].Address)
	t.Logf("With passphrase:    %s", addresses2[0].Address)
}
