package wallet

import (
	"errors"
	"testing"

	"hfb_sys/backend/internal/model"
)

// TestAppendEntriesLogic 测试 AppendEntries 核心逻辑（不依赖数据库）
func TestAppendEntriesValidatesEntry(t *testing.T) {
	entry := Entry{
		UserID:      1001,
		Direction:   "in",
		AmountCent:  10000,
		BalanceType: "available",
		BizType:     "test",
		BizNo:       "TEST001",
	}

	if entry.UserID == 0 {
		t.Fatal("UserID should not be 0")
	}
	if entry.AmountCent <= 0 {
		t.Fatal("AmountCent should be positive")
	}
	if entry.Direction != "in" && entry.Direction != "out" {
		t.Fatal("Direction should be 'in' or 'out'")
	}
	if entry.BalanceType != "available" && entry.BalanceType != "frozen" {
		t.Fatal("BalanceType should be 'available' or 'frozen'")
	}
}

// TestApplyEntryAvailableBalanceIn 测试可用余额入账
func TestApplyEntryAvailableBalanceIn(t *testing.T) {
	account := &model.WalletAccount{
		UserID:               1001,
		AvailableBalanceCent: 5000,
		FrozenBalanceCent:    2000,
	}

	entry := Entry{
		UserID:      1001,
		Direction:   "in",
		AmountCent:  3000,
		BalanceType: "available",
	}

	balanceAfter, err := applyEntry(account, entry)
	if err != nil {
		t.Fatalf("applyEntry() error = %v", err)
	}

	if account.AvailableBalanceCent != 8000 {
		t.Fatalf("AvailableBalanceCent = %d, want 8000", account.AvailableBalanceCent)
	}
	if balanceAfter != 8000 {
		t.Fatalf("balanceAfter = %d, want 8000", balanceAfter)
	}
	if account.FrozenBalanceCent != 2000 {
		t.Fatalf("FrozenBalanceCent changed to %d, want unchanged 2000", account.FrozenBalanceCent)
	}
}

// TestApplyEntryAvailableBalanceOut 测试可用余额出账
func TestApplyEntryAvailableBalanceOut(t *testing.T) {
	account := &model.WalletAccount{
		UserID:               1001,
		AvailableBalanceCent: 5000,
		FrozenBalanceCent:    2000,
	}

	entry := Entry{
		UserID:      1001,
		Direction:   "out",
		AmountCent:  3000,
		BalanceType: "available",
	}

	balanceAfter, err := applyEntry(account, entry)
	if err != nil {
		t.Fatalf("applyEntry() error = %v", err)
	}

	if account.AvailableBalanceCent != 2000 {
		t.Fatalf("AvailableBalanceCent = %d, want 2000", account.AvailableBalanceCent)
	}
	if balanceAfter != 2000 {
		t.Fatalf("balanceAfter = %d, want 2000", balanceAfter)
	}
}

// TestApplyEntryFrozenBalanceIn 测试冻结余额入账
func TestApplyEntryFrozenBalanceIn(t *testing.T) {
	account := &model.WalletAccount{
		UserID:               1001,
		AvailableBalanceCent: 5000,
		FrozenBalanceCent:    2000,
	}

	entry := Entry{
		UserID:      1001,
		Direction:   "in",
		AmountCent:  1000,
		BalanceType: "frozen",
	}

	balanceAfter, err := applyEntry(account, entry)
	if err != nil {
		t.Fatalf("applyEntry() error = %v", err)
	}

	if account.FrozenBalanceCent != 3000 {
		t.Fatalf("FrozenBalanceCent = %d, want 3000", account.FrozenBalanceCent)
	}
	if balanceAfter != 3000 {
		t.Fatalf("balanceAfter = %d, want 3000", balanceAfter)
	}
	if account.AvailableBalanceCent != 5000 {
		t.Fatalf("AvailableBalanceCent changed to %d, want unchanged 5000", account.AvailableBalanceCent)
	}
}

// TestApplyEntryFrozenBalanceOut 测试冻结余额出账
func TestApplyEntryFrozenBalanceOut(t *testing.T) {
	account := &model.WalletAccount{
		UserID:               1001,
		AvailableBalanceCent: 5000,
		FrozenBalanceCent:    3000,
	}

	entry := Entry{
		UserID:      1001,
		Direction:   "out",
		AmountCent:  1500,
		BalanceType: "frozen",
	}

	balanceAfter, err := applyEntry(account, entry)
	if err != nil {
		t.Fatalf("applyEntry() error = %v", err)
	}

	if account.FrozenBalanceCent != 1500 {
		t.Fatalf("FrozenBalanceCent = %d, want 1500", account.FrozenBalanceCent)
	}
	if balanceAfter != 1500 {
		t.Fatalf("balanceAfter = %d, want 1500", balanceAfter)
	}
}

// TestApplyEntryInsufficientAvailableBalance 测试可用余额不足
func TestApplyEntryInsufficientAvailableBalance(t *testing.T) {
	account := &model.WalletAccount{
		UserID:               1001,
		AvailableBalanceCent: 1000,
		FrozenBalanceCent:    2000,
	}

	entry := Entry{
		UserID:      1001,
		Direction:   "out",
		AmountCent:  2000,
		BalanceType: "available",
	}

	_, err := applyEntry(account, entry)
	if !errors.Is(err, ErrInsufficientBalance) {
		t.Fatalf("error = %v, want ErrInsufficientBalance", err)
	}

	// 验证余额未变化
	if account.AvailableBalanceCent != 1000 {
		t.Fatalf("AvailableBalanceCent = %d, should remain 1000 on error", account.AvailableBalanceCent)
	}
}

// TestApplyEntryInsufficientFrozenBalance 测试冻结余额不足
func TestApplyEntryInsufficientFrozenBalance(t *testing.T) {
	account := &model.WalletAccount{
		UserID:               1001,
		AvailableBalanceCent: 5000,
		FrozenBalanceCent:    1000,
	}

	entry := Entry{
		UserID:      1001,
		Direction:   "out",
		AmountCent:  2000,
		BalanceType: "frozen",
	}

	_, err := applyEntry(account, entry)
	if !errors.Is(err, ErrInsufficientBalance) {
		t.Fatalf("error = %v, want ErrInsufficientBalance", err)
	}

	// 验证余额未变化
	if account.FrozenBalanceCent != 1000 {
		t.Fatalf("FrozenBalanceCent = %d, should remain 1000 on error", account.FrozenBalanceCent)
	}
}

// TestApplyEntryMultipleOperations 测试连续多次操作
func TestApplyEntryMultipleOperations(t *testing.T) {
	account := &model.WalletAccount{
		UserID:               1001,
		AvailableBalanceCent: 0,
		FrozenBalanceCent:    0,
	}

	operations := []struct {
		entry   Entry
		wantAvl int64
		wantFrz int64
	}{
		{
			entry:   Entry{UserID: 1001, Direction: "in", AmountCent: 10000, BalanceType: "available"},
			wantAvl: 10000,
			wantFrz: 0,
		},
		{
			entry:   Entry{UserID: 1001, Direction: "in", AmountCent: 5000, BalanceType: "frozen"},
			wantAvl: 10000,
			wantFrz: 5000,
		},
		{
			entry:   Entry{UserID: 1001, Direction: "out", AmountCent: 3000, BalanceType: "available"},
			wantAvl: 7000,
			wantFrz: 5000,
		},
		{
			entry:   Entry{UserID: 1001, Direction: "out", AmountCent: 2000, BalanceType: "frozen"},
			wantAvl: 7000,
			wantFrz: 3000,
		},
	}

	for i, op := range operations {
		_, err := applyEntry(account, op.entry)
		if err != nil {
			t.Fatalf("operation %d: applyEntry() error = %v", i, err)
		}

		if account.AvailableBalanceCent != op.wantAvl {
			t.Fatalf("operation %d: AvailableBalanceCent = %d, want %d",
				i, account.AvailableBalanceCent, op.wantAvl)
		}
		if account.FrozenBalanceCent != op.wantFrz {
			t.Fatalf("operation %d: FrozenBalanceCent = %d, want %d",
				i, account.FrozenBalanceCent, op.wantFrz)
		}
	}
}

// TestApplyEntryEdgeCases 测试边界情况
func TestApplyEntryEdgeCases(t *testing.T) {
	testCases := []struct {
		name    string
		account *model.WalletAccount
		entry   Entry
		wantErr error
	}{
		{
			name: "余额刚好够扣",
			account: &model.WalletAccount{
				UserID:               1001,
				AvailableBalanceCent: 1000,
			},
			entry: Entry{
				UserID:      1001,
				Direction:   "out",
				AmountCent:  1000,
				BalanceType: "available",
			},
			wantErr: nil,
		},
		{
			name: "余额差1分不够扣",
			account: &model.WalletAccount{
				UserID:               1001,
				AvailableBalanceCent: 999,
			},
			entry: Entry{
				UserID:      1001,
				Direction:   "out",
				AmountCent:  1000,
				BalanceType: "available",
			},
			wantErr: ErrInsufficientBalance,
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			_, err := applyEntry(tc.account, tc.entry)
			if !errors.Is(err, tc.wantErr) {
				t.Fatalf("error = %v, want %v", err, tc.wantErr)
			}
		})
	}
}
