package wallet_test

import (
	"context"
	"strings"
	"testing"
	"time"

	"hyapp/pkg/xerr"
	"hyapp/services/wallet-service/internal/domain/ledger"
	walletservice "hyapp/services/wallet-service/internal/service/wallet"
)

func TestVerifyCoinSellerRechargeReceiptMifaPayReturnsPaidSnapshot(t *testing.T) {
	mifaPay := &fakeMifaPayClient{
		queryResp: walletservice.MifaPayQueryOrderResponse{
			OrderID:         "receipt-mifa-1",
			ProviderOrderID: "mb_receipt_mifa_1",
			Status:          "1",
			Amount:          "125000",
			Currency:        "PHP",
			PayType:         "GCASH",
			RawJSON:         `{"status":"1"}`,
		},
	}
	svc := walletservice.New(nil)
	svc.SetMifaPayClient(mifaPay)
	receipt, err := svc.VerifyCoinSellerRechargeReceipt(context.Background(), ledger.VerifyCoinSellerRechargeReceiptCommand{
		AppCode:              "lalu",
		ProviderCode:         ledger.PaymentProviderMifaPay,
		ExternalOrderNo:      "receipt-mifa-1",
		OrderDateMS:          1767225600000,
		ProviderCurrencyCode: "php",
		ProviderAmountMinor:  125000,
		ProviderPayType:      "gcash",
	})
	if err != nil {
		t.Fatalf("VerifyCoinSellerRechargeReceipt mifapay failed: %v", err)
	}
	if !receipt.Verified || receipt.ProviderOrderID != "mb_receipt_mifa_1" || receipt.ProviderAmountMinor != 125000 || receipt.CurrencyCode != "PHP" || receipt.RawJSON == "" {
		t.Fatalf("mifapay receipt snapshot mismatch: %+v", receipt)
	}
	if mifaPay.queryReq.OrderID != "receipt-mifa-1" || mifaPay.queryReq.OrderCreatedAtMS != 1767225600000 {
		t.Fatalf("mifapay query request mismatch: %+v", mifaPay.queryReq)
	}
}

func TestVerifyCoinSellerRechargeReceiptMifaPayRejectsAmountMismatch(t *testing.T) {
	mifaPay := &fakeMifaPayClient{
		queryResp: walletservice.MifaPayQueryOrderResponse{
			OrderID:  "receipt-mifa-amount",
			Status:   "1",
			Amount:   "125000",
			Currency: "PHP",
			RawJSON:  `{"status":"1"}`,
		},
	}
	svc := walletservice.New(nil)
	svc.SetMifaPayClient(mifaPay)
	receipt, err := svc.VerifyCoinSellerRechargeReceipt(context.Background(), ledger.VerifyCoinSellerRechargeReceiptCommand{
		AppCode:              "lalu",
		ProviderCode:         ledger.PaymentProviderMifaPay,
		ExternalOrderNo:      "receipt-mifa-amount",
		OrderDateMS:          1767225600000,
		ProviderCurrencyCode: "PHP",
		ProviderAmountMinor:  125001,
	})
	if err != nil {
		t.Fatalf("VerifyCoinSellerRechargeReceipt mifapay amount mismatch failed: %v", err)
	}
	if receipt.Verified || receipt.FailureReason != "mifapay amount mismatch" {
		t.Fatalf("mifapay amount mismatch should return failed snapshot, got %+v", receipt)
	}
}

func TestVerifyCoinSellerRechargeReceiptMifaPayConvertsINRMajorAmount(t *testing.T) {
	mifaPay := &fakeMifaPayClient{
		queryResp: walletservice.MifaPayQueryOrderResponse{
			OrderID:  "receipt-mifa-inr",
			Status:   "1",
			Amount:   "100",
			Currency: "INR",
			RawJSON:  `{"status":"1","amount":"100","currency":"INR"}`,
		},
	}
	svc := walletservice.New(nil)
	svc.SetMifaPayClient(mifaPay)
	receipt, err := svc.VerifyCoinSellerRechargeReceipt(context.Background(), ledger.VerifyCoinSellerRechargeReceiptCommand{
		AppCode:              "lalu",
		ProviderCode:         ledger.PaymentProviderMifaPay,
		ExternalOrderNo:      "receipt-mifa-inr",
		OrderDateMS:          1767225600000,
		ProviderCurrencyCode: "INR",
		ProviderAmountMinor:  10000,
	})
	if err != nil {
		t.Fatalf("VerifyCoinSellerRechargeReceipt mifapay INR failed: %v", err)
	}
	if !receipt.Verified || receipt.ProviderAmountMinor != 10000 {
		t.Fatalf("mifapay INR whole-major amount should verify as local minor amount, got %+v", receipt)
	}
}

func TestVerifyCoinSellerRechargeReceiptMifaPayRejectsInvalidAmountWhenExpected(t *testing.T) {
	mifaPay := &fakeMifaPayClient{
		queryResp: walletservice.MifaPayQueryOrderResponse{
			OrderID:  "receipt-mifa-bad-amount",
			Status:   "1",
			Amount:   "bad",
			Currency: "PHP",
			RawJSON:  `{"status":"1","amount":"bad"}`,
		},
	}
	svc := walletservice.New(nil)
	svc.SetMifaPayClient(mifaPay)
	receipt, err := svc.VerifyCoinSellerRechargeReceipt(context.Background(), ledger.VerifyCoinSellerRechargeReceiptCommand{
		AppCode:              "lalu",
		ProviderCode:         ledger.PaymentProviderMifaPay,
		ExternalOrderNo:      "receipt-mifa-bad-amount",
		OrderDateMS:          1767225600000,
		ProviderCurrencyCode: "PHP",
		ProviderAmountMinor:  125000,
	})
	if err != nil {
		t.Fatalf("VerifyCoinSellerRechargeReceipt mifapay invalid amount failed: %v", err)
	}
	if receipt.Verified || receipt.FailureReason != "mifapay amount is invalid" {
		t.Fatalf("mifapay invalid amount should return failed snapshot, got %+v", receipt)
	}
}

func TestVerifyCoinSellerRechargeReceiptMifaPayRejectsInvalidAmountWithoutExpected(t *testing.T) {
	mifaPay := &fakeMifaPayClient{
		queryResp: walletservice.MifaPayQueryOrderResponse{
			OrderID:  "receipt-mifa-empty-amount",
			Status:   "1",
			Amount:   "",
			Currency: "PHP",
			RawJSON:  `{"status":"1","amount":""}`,
		},
	}
	svc := walletservice.New(nil)
	svc.SetMifaPayClient(mifaPay)
	receipt, err := svc.VerifyCoinSellerRechargeReceipt(context.Background(), ledger.VerifyCoinSellerRechargeReceiptCommand{
		AppCode:              "lalu",
		ProviderCode:         ledger.PaymentProviderMifaPay,
		ExternalOrderNo:      "receipt-mifa-empty-amount",
		OrderDateMS:          1767225600000,
		ProviderCurrencyCode: "PHP",
	})
	if err != nil {
		t.Fatalf("VerifyCoinSellerRechargeReceipt mifapay empty amount failed: %v", err)
	}
	if receipt.Verified || receipt.FailureReason != "mifapay amount is invalid" {
		t.Fatalf("mifapay empty amount should return failed snapshot, got %+v", receipt)
	}
}

func TestVerifyCoinSellerRechargeReceiptMifaPayWithoutDateUsesRecentLookup(t *testing.T) {
	mifaPay := &fakeMifaPayClient{
		querySeq: []fakeMifaPayQueryResult{
			{err: xerr.New(xerr.NotFound, "mifapay order not found")},
			{resp: walletservice.MifaPayQueryOrderResponse{
				OrderID:         "receipt-mifa-no-date",
				ProviderOrderID: "mb_receipt_mifa_no_date",
				Status:          "1",
				Amount:          "125000",
				Currency:        "PHP",
				PayType:         "GCASH",
				RawJSON:         `{"status":"1"}`,
			}},
		},
	}
	svc := walletservice.New(nil)
	svc.SetMifaPayClient(mifaPay)
	receipt, err := svc.VerifyCoinSellerRechargeReceipt(context.Background(), ledger.VerifyCoinSellerRechargeReceiptCommand{
		AppCode:         "lalu",
		ProviderCode:    ledger.PaymentProviderMifaPay,
		ExternalOrderNo: "receipt-mifa-no-date",
	})
	if err != nil {
		t.Fatalf("VerifyCoinSellerRechargeReceipt mifapay without date failed: %v", err)
	}
	if !receipt.Verified || receipt.ProviderOrderID != "mb_receipt_mifa_no_date" {
		t.Fatalf("mifapay no-date receipt snapshot mismatch: %+v", receipt)
	}
	if len(mifaPay.queryReqs) != 2 {
		t.Fatalf("mifapay no-date lookup should continue after not found, got %+v", mifaPay.queryReqs)
	}
	firstDate := time.UnixMilli(mifaPay.queryReqs[0].OrderCreatedAtMS).UTC()
	secondDate := time.UnixMilli(mifaPay.queryReqs[1].OrderCreatedAtMS).UTC()
	if firstDate.Hour() != 0 || secondDate.Hour() != 0 || !sameUTCDate(secondDate, firstDate.AddDate(0, 0, -1)) {
		t.Fatalf("mifapay no-date lookup should walk backwards by UTC date: %+v", mifaPay.queryReqs)
	}
}

func TestVerifyCoinSellerRechargeReceiptMifaPayWithoutDateReturnsUnpaidSnapshot(t *testing.T) {
	mifaPay := &fakeMifaPayClient{
		querySeq: []fakeMifaPayQueryResult{
			{err: xerr.New(xerr.NotFound, "mifapay order not found")},
			{resp: walletservice.MifaPayQueryOrderResponse{
				OrderID:  "receipt-mifa-unpaid",
				Status:   "0",
				Amount:   "125000",
				Currency: "PHP",
				RawJSON:  `{"status":"0"}`,
			}},
		},
	}
	svc := walletservice.New(nil)
	svc.SetMifaPayClient(mifaPay)
	receipt, err := svc.VerifyCoinSellerRechargeReceipt(context.Background(), ledger.VerifyCoinSellerRechargeReceiptCommand{
		AppCode:         "lalu",
		ProviderCode:    ledger.PaymentProviderMifaPay,
		ExternalOrderNo: "receipt-mifa-unpaid",
	})
	if err != nil {
		t.Fatalf("VerifyCoinSellerRechargeReceipt mifapay unpaid failed: %v", err)
	}
	if receipt.Verified || receipt.FailureReason != "mifapay order status 0" {
		t.Fatalf("mifapay unpaid should return failed snapshot, got %+v", receipt)
	}
	if len(mifaPay.queryReqs) != 2 {
		t.Fatalf("mifapay unpaid should stop once an order is found, got %+v", mifaPay.queryReqs)
	}
}

func TestVerifyCoinSellerRechargeReceiptV5PayRejectsCurrencyMismatchWithoutWriting(t *testing.T) {
	v5Pay := &fakeV5PayClient{
		queryResp: walletservice.V5PayQueryOrderResponse{
			OrderID:         "receipt-v5-1",
			ProviderOrderID: "tx_receipt_v5_1",
			Status:          "2",
			Amount:          "48.00",
			Currency:        "TRY",
			ProductType:     "BANK_TRANSFER",
			RawJSON:         `{"status":"2"}`,
		},
	}
	svc := walletservice.New(nil)
	svc.SetV5PayClient(v5Pay)
	receipt, err := svc.VerifyCoinSellerRechargeReceipt(context.Background(), ledger.VerifyCoinSellerRechargeReceiptCommand{
		AppCode:              "lalu",
		ProviderCode:         ledger.PaymentProviderV5Pay,
		ExternalOrderNo:      "receipt-v5-1",
		ProviderCountryCode:  "TR",
		ProviderCurrencyCode: "PHP",
		ProviderAmountMinor:  4800,
		ProviderPayType:      "BANK_TRANSFER",
	})
	if err != nil {
		t.Fatalf("VerifyCoinSellerRechargeReceipt v5pay failed: %v", err)
	}
	if receipt.Verified || receipt.FailureReason != "v5pay currency mismatch" || receipt.ProviderAmountMinor != 4800 {
		t.Fatalf("v5pay mismatch should return a failed snapshot, got %+v", receipt)
	}
	if v5Pay.queryReq.CountryCode != "TR" || v5Pay.queryReq.CurrencyCode != "PHP" {
		t.Fatalf("v5pay query request should use caller supplied country/currency: %+v", v5Pay.queryReq)
	}
}

func TestVerifyCoinSellerRechargeReceiptV5PayRejectsAmountMismatch(t *testing.T) {
	v5Pay := &fakeV5PayClient{
		queryResp: walletservice.V5PayQueryOrderResponse{
			OrderID:         "receipt-v5-amount",
			ProviderOrderID: "tx_receipt_v5_amount",
			Status:          "2",
			Amount:          "48.00",
			Currency:        "TRY",
			ProductType:     "BANK_TRANSFER",
			RawJSON:         `{"status":"2"}`,
		},
	}
	svc := walletservice.New(nil)
	svc.SetV5PayClient(v5Pay)
	receipt, err := svc.VerifyCoinSellerRechargeReceipt(context.Background(), ledger.VerifyCoinSellerRechargeReceiptCommand{
		AppCode:              "lalu",
		ProviderCode:         ledger.PaymentProviderV5Pay,
		ExternalOrderNo:      "receipt-v5-amount",
		ProviderCountryCode:  "TR",
		ProviderCurrencyCode: "TRY",
		ProviderAmountMinor:  4700,
		ProviderPayType:      "BANK_TRANSFER",
	})
	if err != nil {
		t.Fatalf("VerifyCoinSellerRechargeReceipt v5pay amount mismatch failed: %v", err)
	}
	if receipt.Verified || receipt.FailureReason != "v5pay amount mismatch" || receipt.ProviderAmountMinor != 4800 {
		t.Fatalf("v5pay amount mismatch should return a failed snapshot, got %+v", receipt)
	}
}

func TestVerifyCoinSellerRechargeReceiptV5PayRejectsInvalidAmountWhenExpected(t *testing.T) {
	v5Pay := &fakeV5PayClient{
		queryResp: walletservice.V5PayQueryOrderResponse{
			OrderID:         "receipt-v5-bad-amount",
			ProviderOrderID: "tx_receipt_v5_bad_amount",
			Status:          "2",
			Amount:          "bad",
			Currency:        "TRY",
			ProductType:     "BANK_TRANSFER",
			RawJSON:         `{"status":"2","amount":"bad"}`,
		},
	}
	svc := walletservice.New(nil)
	svc.SetV5PayClient(v5Pay)
	receipt, err := svc.VerifyCoinSellerRechargeReceipt(context.Background(), ledger.VerifyCoinSellerRechargeReceiptCommand{
		AppCode:              "lalu",
		ProviderCode:         ledger.PaymentProviderV5Pay,
		ExternalOrderNo:      "receipt-v5-bad-amount",
		ProviderCountryCode:  "TR",
		ProviderCurrencyCode: "TRY",
		ProviderAmountMinor:  4800,
		ProviderPayType:      "BANK_TRANSFER",
	})
	if err != nil {
		t.Fatalf("VerifyCoinSellerRechargeReceipt v5pay invalid amount failed: %v", err)
	}
	if receipt.Verified || receipt.FailureReason != "v5pay amount is invalid" {
		t.Fatalf("v5pay invalid amount should return a failed snapshot, got %+v", receipt)
	}
}

func TestVerifyCoinSellerRechargeReceiptV5PayRejectsInvalidAmountWithoutExpected(t *testing.T) {
	v5Pay := &fakeV5PayClient{
		queryResp: walletservice.V5PayQueryOrderResponse{
			OrderID:         "receipt-v5-empty-amount",
			ProviderOrderID: "tx_receipt_v5_empty_amount",
			Status:          "2",
			Amount:          "",
			Currency:        "TRY",
			ProductType:     "BANK_TRANSFER",
			RawJSON:         `{"status":"2","amount":""}`,
		},
	}
	svc := walletservice.New(nil)
	svc.SetV5PayClient(v5Pay)
	receipt, err := svc.VerifyCoinSellerRechargeReceipt(context.Background(), ledger.VerifyCoinSellerRechargeReceiptCommand{
		AppCode:              "lalu",
		ProviderCode:         ledger.PaymentProviderV5Pay,
		ExternalOrderNo:      "receipt-v5-empty-amount",
		ProviderCountryCode:  "TR",
		ProviderCurrencyCode: "TRY",
		ProviderPayType:      "BANK_TRANSFER",
	})
	if err != nil {
		t.Fatalf("VerifyCoinSellerRechargeReceipt v5pay empty amount failed: %v", err)
	}
	if receipt.Verified || receipt.FailureReason != "v5pay amount is invalid" {
		t.Fatalf("v5pay empty amount should return failed snapshot, got %+v", receipt)
	}
}

func TestVerifyCoinSellerRechargeReceiptV5PayConvertsWholeMajorAmount(t *testing.T) {
	v5Pay := &fakeV5PayClient{
		queryResp: walletservice.V5PayQueryOrderResponse{
			OrderID:         "receipt-v5-whole-major",
			ProviderOrderID: "tx_receipt_v5_whole_major",
			Status:          "2",
			Amount:          "100",
			Currency:        "TRY",
			ProductType:     "BANK_TRANSFER",
			RawJSON:         `{"status":"2","amount":"100"}`,
		},
	}
	svc := walletservice.New(nil)
	svc.SetV5PayClient(v5Pay)
	receipt, err := svc.VerifyCoinSellerRechargeReceipt(context.Background(), ledger.VerifyCoinSellerRechargeReceiptCommand{
		AppCode:              "lalu",
		ProviderCode:         ledger.PaymentProviderV5Pay,
		ExternalOrderNo:      "receipt-v5-whole-major",
		ProviderCountryCode:  "TR",
		ProviderCurrencyCode: "TRY",
		ProviderAmountMinor:  10000,
		ProviderPayType:      "BANK_TRANSFER",
	})
	if err != nil {
		t.Fatalf("VerifyCoinSellerRechargeReceipt v5pay whole major amount failed: %v", err)
	}
	if !receipt.Verified || receipt.ProviderAmountMinor != 10000 {
		t.Fatalf("v5pay whole major amount should verify as local minor amount, got %+v", receipt)
	}
}

func TestVerifyCoinSellerRechargeReceiptV5PayWithoutCountryCurrencyUsesEnabledCandidates(t *testing.T) {
	repository := &fakeReceiptRepository{channels: []ledger.ThirdPartyPaymentChannel{{
		AppCode:      "lalu",
		ProviderCode: ledger.PaymentProviderV5Pay,
		Status:       ledger.ThirdPartyPaymentStatusActive,
		Methods: []ledger.ThirdPartyPaymentMethod{
			{ProviderCode: ledger.PaymentProviderV5Pay, CountryCode: "TR", CurrencyCode: "TRY", Status: ledger.ThirdPartyPaymentStatusActive},
			{ProviderCode: ledger.PaymentProviderV5Pay, CountryCode: "PH", CurrencyCode: "PHP", Status: ledger.ThirdPartyPaymentStatusActive},
		},
	}}}
	v5Pay := &fakeV5PayClient{
		querySeq: []fakeV5PayQueryResult{
			{err: xerr.New(xerr.NotFound, "v5pay order not found")},
			{resp: walletservice.V5PayQueryOrderResponse{
				OrderID:         "receipt-v5-no-hints",
				ProviderOrderID: "tx_receipt_v5_no_hints",
				Status:          "2",
				Amount:          "48.00",
				Currency:        "PHP",
				ProductType:     "GCASH",
				RawJSON:         `{"status":"2"}`,
			}},
		},
	}
	svc := walletservice.New(repository)
	svc.SetV5PayClient(v5Pay)
	receipt, err := svc.VerifyCoinSellerRechargeReceipt(context.Background(), ledger.VerifyCoinSellerRechargeReceiptCommand{
		AppCode:         "lalu",
		ProviderCode:    ledger.PaymentProviderV5Pay,
		ExternalOrderNo: "receipt-v5-no-hints",
	})
	if err != nil {
		t.Fatalf("VerifyCoinSellerRechargeReceipt v5pay no hints failed: %v", err)
	}
	if !receipt.Verified || receipt.ProviderOrderID != "tx_receipt_v5_no_hints" {
		t.Fatalf("v5pay no-hints receipt snapshot mismatch: %+v", receipt)
	}
	if !repository.querySeen || repository.query.ProviderCode != ledger.PaymentProviderV5Pay || repository.query.IncludeDisabledMethods {
		t.Fatalf("v5pay no-hints lookup should enumerate active V5Pay methods: %+v", repository.query)
	}
	if len(v5Pay.queryReqs) != 2 || v5Pay.queryReqs[0].CountryCode != "TR" || v5Pay.queryReqs[0].CurrencyCode != "TRY" || v5Pay.queryReqs[1].CountryCode != "PH" || v5Pay.queryReqs[1].CurrencyCode != "PHP" {
		t.Fatalf("v5pay no-hints lookup should use enabled country/currency candidates: %+v", v5Pay.queryReqs)
	}
}

func TestVerifyCoinSellerRechargeReceiptV5PayUsesLaluCountryCurrencyConfig(t *testing.T) {
	repository := &fakeReceiptRepository{channels: []ledger.ThirdPartyPaymentChannel{{
		AppCode:      "lalu",
		ProviderCode: ledger.PaymentProviderV5Pay,
		Status:       ledger.ThirdPartyPaymentStatusActive,
		Methods: []ledger.ThirdPartyPaymentMethod{{
			ProviderCode: ledger.PaymentProviderV5Pay,
			CountryCode:  "TR",
			CurrencyCode: "TRY",
			Status:       ledger.ThirdPartyPaymentStatusActive,
		}},
	}}}
	v5Pay := &fakeV5PayClient{queryResp: walletservice.V5PayQueryOrderResponse{
		OrderID:         "receipt-v5-yumi-shared-config",
		ProviderOrderID: "tx_receipt_v5_yumi_shared_config",
		Status:          "2",
		Amount:          "48.00",
		Currency:        "TRY",
		ProductType:     "BANK_TRANSFER",
		RawJSON:         `{"status":"2"}`,
	}}
	svc := walletservice.New(repository)
	svc.SetV5PayClient(v5Pay)
	receipt, err := svc.VerifyCoinSellerRechargeReceipt(context.Background(), ledger.VerifyCoinSellerRechargeReceiptCommand{
		AppCode:         "yumi",
		ProviderCode:    ledger.PaymentProviderV5Pay,
		ExternalOrderNo: "receipt-v5-yumi-shared-config",
	})
	if err != nil {
		t.Fatalf("VerifyCoinSellerRechargeReceipt v5pay shared config failed: %v", err)
	}
	if !receipt.Verified || repository.query.AppCode != "lalu" {
		t.Fatalf("V5Pay receipt must query Lalu country/currency config, receipt=%+v query=%+v", receipt, repository.query)
	}
	if len(v5Pay.queryReqs) != 1 || v5Pay.queryReqs[0].CountryCode != "TR" || v5Pay.queryReqs[0].CurrencyCode != "TRY" {
		t.Fatalf("V5Pay receipt must use the Lalu candidate, got %+v", v5Pay.queryReqs)
	}
}

func TestVerifyCoinSellerRechargeReceiptV5PayWithoutCountryCurrencyNotFound(t *testing.T) {
	repository := &fakeReceiptRepository{channels: []ledger.ThirdPartyPaymentChannel{{
		AppCode:      "lalu",
		ProviderCode: ledger.PaymentProviderV5Pay,
		Status:       ledger.ThirdPartyPaymentStatusActive,
		Methods: []ledger.ThirdPartyPaymentMethod{
			{ProviderCode: ledger.PaymentProviderV5Pay, CountryCode: "TR", CurrencyCode: "TRY", Status: ledger.ThirdPartyPaymentStatusActive},
			{ProviderCode: ledger.PaymentProviderV5Pay, CountryCode: "PH", CurrencyCode: "PHP", Status: ledger.ThirdPartyPaymentStatusActive},
		},
	}}}
	v5Pay := &fakeV5PayClient{querySeq: []fakeV5PayQueryResult{
		{err: xerr.New(xerr.NotFound, "v5pay order not found")},
		{err: xerr.New(xerr.NotFound, "v5pay order not found")},
	}}
	svc := walletservice.New(repository)
	svc.SetV5PayClient(v5Pay)
	receipt, err := svc.VerifyCoinSellerRechargeReceipt(context.Background(), ledger.VerifyCoinSellerRechargeReceiptCommand{
		AppCode:         "lalu",
		ProviderCode:    ledger.PaymentProviderV5Pay,
		ExternalOrderNo: "receipt-v5-missing",
	})
	if err != nil {
		t.Fatalf("VerifyCoinSellerRechargeReceipt v5pay not found failed: %v", err)
	}
	if receipt.Verified || receipt.FailureReason != "v5pay order not found for enabled country/currency candidates" {
		t.Fatalf("v5pay missing order should return failed snapshot, got %+v", receipt)
	}
	if len(v5Pay.queryReqs) != 2 {
		t.Fatalf("v5pay missing order should exhaust enabled candidates, got %+v", v5Pay.queryReqs)
	}
}

func TestVerifyCoinSellerRechargeReceiptUSDTBEP20UsesReadOnlyClient(t *testing.T) {
	bsc := &fakeTronUSDTClient{rawJSON: `{"receipt":true}`}
	svc := walletservice.New(nil)
	svc.SetBSCUSDTClient(bsc)
	svc.SetExternalRechargeConfig(walletservice.ExternalRechargeConfig{
		USDTBEP20Enabled: true,
		USDTBEP20Address: "0xPlatformBEP20ReceiveAddress",
	})
	txHash := "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
	receipt, err := svc.VerifyCoinSellerRechargeReceipt(context.Background(), ledger.VerifyCoinSellerRechargeReceiptCommand{
		AppCode:         "lalu",
		ProviderCode:    ledger.PaymentProviderUSDTBEP20,
		ExternalOrderNo: txHash,
		USDMinorAmount:  150,
	})
	if err != nil {
		t.Fatalf("VerifyCoinSellerRechargeReceipt usdt_bep20 failed: %v", err)
	}
	if !receipt.Verified || receipt.Status != "confirmed" || receipt.Chain != "bep20" || receipt.ProviderAmountMinor != 150 || receipt.ReceiveAddress != "0xPlatformBEP20ReceiveAddress" {
		t.Fatalf("bep20 receipt snapshot mismatch: %+v", receipt)
	}
	if bsc.lastTxHash != txHash || bsc.lastToAddress != "0xPlatformBEP20ReceiveAddress" || bsc.lastAmountMinor != 150 {
		t.Fatalf("bep20 client request mismatch: %+v", bsc)
	}
}

func TestVerifyCoinSellerRechargeReceiptUSDTBEP20DisabledFailsClosed(t *testing.T) {
	svc := walletservice.New(nil)
	_, err := svc.VerifyCoinSellerRechargeReceipt(context.Background(), ledger.VerifyCoinSellerRechargeReceiptCommand{
		AppCode:         "lalu",
		ProviderCode:    ledger.PaymentProviderUSDTBEP20,
		ExternalOrderNo: "0xreceiptbep20",
		USDMinorAmount:  150,
	})
	if !xerr.IsCode(err, xerr.Unavailable) || !strings.Contains(err.Error(), "not configured") {
		t.Fatalf("disabled bep20 verification should fail closed, got %v", err)
	}
}

func TestVerifyCoinSellerRechargeReceiptUSDTTRC20HashUsesTronGrid(t *testing.T) {
	tron := &fakeTronUSDTClient{rawJSON: `{"chain":true}`}
	binance := &fakeBinanceClient{err: xerr.New(xerr.Internal, "binance should not be called")}
	svc := walletservice.New(nil)
	svc.SetTronUSDTClient(tron)
	svc.SetBinanceClient(binance)
	svc.SetExternalRechargeConfig(walletservice.ExternalRechargeConfig{
		USDTTRC20Enabled: true,
		USDTTRC20Address: "TPlatformReceiveAddress",
	})
	txHash := "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
	receipt, err := svc.VerifyCoinSellerRechargeReceipt(context.Background(), ledger.VerifyCoinSellerRechargeReceiptCommand{
		AppCode:         "lalu",
		ProviderCode:    ledger.PaymentProviderUSDTTRC20,
		ExternalOrderNo: txHash,
		USDMinorAmount:  50000,
	})
	if err != nil {
		t.Fatalf("VerifyCoinSellerRechargeReceipt usdt_trc20 hash failed: %v", err)
	}
	if !receipt.Verified || receipt.Status != "confirmed" || receipt.RawJSON != `{"chain":true}` {
		t.Fatalf("trc20 hash receipt snapshot mismatch: %+v", receipt)
	}
	if tron.lastTxHash != txHash || tron.lastToAddress != "TPlatformReceiveAddress" || tron.lastAmountMinor != 50000 {
		t.Fatalf("trc20 hash should use TronGrid client: %+v", tron)
	}
	if binance.calls != 0 {
		t.Fatalf("chain tx hash must not call Binance, calls=%d", binance.calls)
	}
}

func TestVerifyCoinSellerRechargeReceiptUSDTTRC20PrefersSQLAddress(t *testing.T) {
	tron := &fakeTronUSDTClient{rawJSON: `{"chain":true}`}
	svc := walletservice.New(nil)
	svc.SetTronUSDTClient(tron)
	svc.SetExternalRechargeConfig(walletservice.ExternalRechargeConfig{
		USDTTRC20Enabled: true,
		USDTTRC20Address: "TYAMLAddressMustNotWin",
	})
	txHash := "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
	receipt, err := svc.VerifyCoinSellerRechargeReceipt(context.Background(), ledger.VerifyCoinSellerRechargeReceiptCommand{
		AppCode:         "aslan",
		ProviderCode:    ledger.PaymentProviderUSDTTRC20,
		ExternalOrderNo: txHash,
		USDMinorAmount:  30014,
		ReceiveAddress:  "TSQLAddressMustWin",
	})
	if err != nil {
		t.Fatalf("TRC20 SQL address override failed: %v", err)
	}
	if !receipt.Verified || receipt.ReceiveAddress != "TSQLAddressMustWin" || tron.lastToAddress != "TSQLAddressMustWin" {
		t.Fatalf("current SQL address must drive verification immediately: receipt=%+v client=%+v", receipt, tron)
	}
}

func TestVerifyCoinSellerRechargeReceiptUSDTTRC20RejectsC2COrderNumber(t *testing.T) {
	svc := walletservice.New(nil)
	svc.SetTronUSDTClient(&fakeTronUSDTClient{})
	svc.SetExternalRechargeConfig(walletservice.ExternalRechargeConfig{
		USDTTRC20Enabled: true,
		USDTTRC20Address: "TPlatformReceiveAddress",
	})
	_, err := svc.VerifyCoinSellerRechargeReceipt(context.Background(), ledger.VerifyCoinSellerRechargeReceiptCommand{
		AppCode:         "lalu",
		ProviderCode:    ledger.PaymentProviderUSDTTRC20,
		ExternalOrderNo: "440537037787594752",
		USDMinorAmount:  20000,
	})
	if !xerr.IsCode(err, xerr.InvalidArgument) || !strings.Contains(err.Error(), "requires transaction hash") {
		t.Fatalf("TRC20 must reject C2C order number without fallback: %v", err)
	}
}

func TestVerifyCoinSellerRechargeReceiptUSDTC2CUsesBinance(t *testing.T) {
	tron := &fakeTronUSDTClient{err: xerr.New(xerr.Internal, "tron should not be called")}
	binance := &fakeBinanceClient{record: walletservice.BinanceTransferRecord{
		TxID:        "Off-chain transfer 388611987194",
		Coin:        ledger.PaidCurrencyUSDT,
		Network:     "TRX",
		Status:      1,
		Address:     "TNwqCkF8cnM4XiwjnbbKTfdZvFDqVoa1TN",
		Amount:      "500.00000000",
		AmountMinor: 50000,
		RawJSON:     `{"txId":"Off-chain transfer 388611987194","status":1}`,
	}}
	svc := walletservice.New(nil)
	svc.SetTronUSDTClient(tron)
	svc.SetBinanceClient(binance)
	svc.SetExternalRechargeConfig(walletservice.ExternalRechargeConfig{
		USDTTRC20Enabled: true,
		USDTTRC20Address: "TGlobalFallbackReceiveAddress",
		USDTTRC20Addresses: map[string]string{
			"aslan": "TNwqCkF8cnM4XiwjnbbKTfdZvFDqVoa1TN",
		},
	})
	receipt, err := svc.VerifyCoinSellerRechargeReceipt(context.Background(), ledger.VerifyCoinSellerRechargeReceiptCommand{
		AppCode:         "aslan",
		ProviderCode:    ledger.PaymentProviderUSDTC2C,
		ExternalOrderNo: "388611987194",
		USDMinorAmount:  50000,
		OrderDateMS:     1783344000000,
	})
	if err != nil {
		t.Fatalf("VerifyCoinSellerRechargeReceipt binance offchain failed: %v", err)
	}
	if !receipt.Verified || receipt.Status != "confirmed" || receipt.ProviderOrderID != "Off-chain transfer 388611987194" || receipt.ProviderAmountMinor != 50000 || receipt.ReceiveAddress != "" {
		t.Fatalf("binance offchain receipt snapshot mismatch: %+v", receipt)
	}
	if binance.calls != 1 || binance.req.AppCode != "aslan" || binance.req.ExternalID != "388611987194" || binance.req.Network != "C2C" || binance.req.ToAddress != "" || binance.req.AmountMinor != 50000 || binance.req.OrderTimeMS != 1783344000000 {
		t.Fatalf("binance offchain request mismatch: calls=%d req=%+v", binance.calls, binance.req)
	}
	if tron.lastTxHash != "" {
		t.Fatalf("off-chain Binance id must not call TronGrid: %+v", tron)
	}
}

func TestVerifyCoinSellerRechargeReceiptUSDTOffchainUsesSQLTRONAddress(t *testing.T) {
	const address = "TLv7wERsM42ZeobMrrKVC5C72d3gFUa44J"
	binance := &fakeBinanceClient{record: walletservice.BinanceTransferRecord{
		Source:      "deposit_history",
		TxID:        "Off-chain transfer 391252658053",
		Coin:        ledger.PaidCurrencyUSDT,
		Network:     "TRX",
		Status:      1,
		Address:     address,
		Amount:      "300.14000000",
		AmountMinor: 30014,
		RawJSON:     `{"txId":"Off-chain transfer 391252658053","status":1}`,
	}}
	svc := walletservice.New(nil)
	svc.SetBinanceClient(binance)
	svc.SetExternalRechargeConfig(walletservice.ExternalRechargeConfig{
		USDTTRC20Enabled: true,
		USDTTRC20Address: "TYAMLAddressMustNotWin",
	})
	receipt, err := svc.VerifyCoinSellerRechargeReceipt(context.Background(), ledger.VerifyCoinSellerRechargeReceiptCommand{
		AppCode:         "aslan",
		ProviderCode:    ledger.PaymentProviderUSDTOffchain,
		ExternalOrderNo: "391252658053",
		USDMinorAmount:  30014,
		OrderDateMS:     1784033439000,
		ReceiveAddress:  address,
	})
	if err != nil {
		t.Fatalf("explicit Off-chain receipt failed: %v", err)
	}
	if !receipt.Verified || receipt.Chain != "offchain" || receipt.ReceiveAddress != address || receipt.ProviderOrderID != "Off-chain transfer 391252658053" {
		t.Fatalf("Off-chain receipt mismatch: %+v", receipt)
	}
	if binance.calls != 1 || binance.req.Network != "OFFCHAIN" || binance.req.ToAddress != address || binance.req.AmountMinor != 30014 {
		t.Fatalf("Off-chain Binance request mismatch: calls=%d req=%+v", binance.calls, binance.req)
	}
}

func TestVerifyCoinSellerRechargeReceiptUSDTC2CPayKeepsNoChainAddress(t *testing.T) {
	binance := &fakeBinanceClient{record: walletservice.BinanceTransferRecord{
		Source:      "pay_history",
		ID:          "433527303245463552",
		TxID:        "P_A223BU5M4Y171116",
		Coin:        ledger.PaidCurrencyUSDT,
		Status:      1,
		Amount:      "100.00000000",
		AmountMinor: 10000,
		RawJSON:     `{"orderType":"C2C","orderId":"433527303245463552","amount":"100.00000000","currency":"USDT"}`,
	}}
	svc := walletservice.New(nil)
	svc.SetBinanceClient(binance)
	svc.SetExternalRechargeConfig(walletservice.ExternalRechargeConfig{
		USDTTRC20Enabled: true,
		USDTTRC20Addresses: map[string]string{
			"yumi": "TYumiConfiguredChainAddress",
		},
	})
	receipt, err := svc.VerifyCoinSellerRechargeReceipt(context.Background(), ledger.VerifyCoinSellerRechargeReceiptCommand{
		AppCode:         "yumi",
		ProviderCode:    ledger.PaymentProviderUSDTC2C,
		ExternalOrderNo: "433527303245463552",
		USDMinorAmount:  10000,
		OrderDateMS:     1779713682799,
	})
	if err != nil {
		t.Fatalf("VerifyCoinSellerRechargeReceipt Pay C2C failed: %v", err)
	}
	if !receipt.Verified || receipt.Status != "confirmed" || receipt.ProviderOrderID != "P_A223BU5M4Y171116" || receipt.ProviderAmountMinor != 10000 {
		t.Fatalf("Pay C2C receipt snapshot mismatch: %+v", receipt)
	}
	if receipt.ReceiveAddress != "" {
		t.Fatalf("Pay C2C must not inherit an unrelated chain address: %+v", receipt)
	}
	if binance.req.OrderTimeMS != 1779713682799 {
		t.Fatalf("coin seller order time must bound Pay History query: %+v", binance.req)
	}
}

func TestVerifyCoinSellerRechargeReceiptUSDTC2CPayUsesBinance(t *testing.T) {
	bsc := &fakeTronUSDTClient{err: xerr.New(xerr.Internal, "bsc chain client should not be called")}
	binance := &fakeBinanceClient{record: walletservice.BinanceTransferRecord{
		Source:      "pay_history",
		ID:          "440537037787594752",
		TxID:        "P_C2C_BEP20_RECEIPT",
		Coin:        ledger.PaidCurrencyUSDT,
		Status:      1,
		Amount:      "200.00000000",
		AmountMinor: 20000,
		RawJSON:     `{"orderType":"C2C","orderId":"440537037787594752","amount":"200.00000000","currency":"USDT"}`,
	}}
	svc := walletservice.New(nil)
	svc.SetBSCUSDTClient(bsc)
	svc.SetBinanceClient(binance)
	svc.SetExternalRechargeConfig(walletservice.ExternalRechargeConfig{
		USDTBEP20Enabled: true,
		USDTBEP20Addresses: map[string]string{
			"yumi": "0xYumiConfiguredBSCAddress",
		},
	})
	receipt, err := svc.VerifyCoinSellerRechargeReceipt(context.Background(), ledger.VerifyCoinSellerRechargeReceiptCommand{
		AppCode:         "yumi",
		ProviderCode:    ledger.PaymentProviderUSDTC2C,
		ExternalOrderNo: "440537037787594752",
		USDMinorAmount:  20000,
		OrderDateMS:     1784017140000,
	})
	if err != nil {
		t.Fatalf("VerifyCoinSellerRechargeReceipt BEP20 Pay C2C failed: %v", err)
	}
	if !receipt.Verified || receipt.Status != "confirmed" || receipt.ProviderOrderID != "P_C2C_BEP20_RECEIPT" || receipt.ProviderAmountMinor != 20000 {
		t.Fatalf("BEP20 Pay C2C receipt snapshot mismatch: %+v", receipt)
	}
	if receipt.ReceiveAddress != "" {
		t.Fatalf("C2C must not persist an unrelated chain address: %+v", receipt)
	}
	if binance.calls != 1 || binance.req.Network != "C2C" || binance.req.ExternalID != "440537037787594752" || binance.req.AmountMinor != 20000 {
		t.Fatalf("C2C must use Binance transfer lookup: calls=%d req=%+v", binance.calls, binance.req)
	}
	if bsc.lastTxHash != "" {
		t.Fatalf("BEP20 Pay C2C order id must not call BSC chain client: %+v", bsc)
	}
}

func TestVerifyCoinSellerRechargeReceiptUSDTC2CDoesNotRequireChainConfig(t *testing.T) {
	binance := &fakeBinanceClient{record: walletservice.BinanceTransferRecord{
		Source:      "pay_history",
		TxID:        "P_C2C_LALU_RECEIPT",
		Coin:        ledger.PaidCurrencyUSDT,
		Status:      1,
		Amount:      "200.00000000",
		AmountMinor: 20000,
	}}
	svc := walletservice.New(nil)
	svc.SetBinanceClient(binance)
	// Lalu 只校验 Binance C2C 收款事实时不需要开启 BSC RPC，也不需要伪造一个链上收款地址。
	svc.SetExternalRechargeConfig(walletservice.ExternalRechargeConfig{})
	receipt, err := svc.VerifyCoinSellerRechargeReceipt(context.Background(), ledger.VerifyCoinSellerRechargeReceiptCommand{
		AppCode:         "lalu",
		ProviderCode:    ledger.PaymentProviderUSDTC2C,
		ExternalOrderNo: "440537037787594752",
		USDMinorAmount:  20000,
		OrderDateMS:     1784017620000,
	})
	if err != nil {
		t.Fatalf("BEP20 Pay C2C must not require chain config: %v", err)
	}
	if !receipt.Verified || receipt.ProviderOrderID != "P_C2C_LALU_RECEIPT" {
		t.Fatalf("BEP20 Pay C2C receipt mismatch without chain config: %+v", receipt)
	}
	if binance.calls != 1 || binance.req.ToAddress != "" || binance.req.Network != "C2C" {
		t.Fatalf("BEP20 Pay C2C lookup mismatch without chain config: calls=%d req=%+v", binance.calls, binance.req)
	}
}

func TestVerifyCoinSellerRechargeReceiptUSDTC2CMissingBinanceAccount(t *testing.T) {
	binance := &fakeBinanceClient{err: xerr.New(xerr.Unavailable, "binance account is not configured")}
	svc := walletservice.New(nil)
	svc.SetBinanceClient(binance)
	svc.SetExternalRechargeConfig(walletservice.ExternalRechargeConfig{
		USDTTRC20Enabled: true,
		USDTTRC20Address: "TNwqCkF8cnM4XiwjnbbKTfdZvFDqVoa1TN",
	})
	receipt, err := svc.VerifyCoinSellerRechargeReceipt(context.Background(), ledger.VerifyCoinSellerRechargeReceiptCommand{
		AppCode:         "yumi",
		ProviderCode:    ledger.PaymentProviderUSDTC2C,
		ExternalOrderNo: "388611987194",
		USDMinorAmount:  50000,
	})
	if err != nil {
		t.Fatalf("missing binance account should return failed snapshot, got error %v", err)
	}
	if receipt.Verified || !strings.Contains(receipt.FailureReason, "binance account is not configured") {
		t.Fatalf("missing binance account should be explicit in snapshot, got %+v", receipt)
	}
}

func TestSubmitH5RechargeTxBinanceOffchainUsesAppScopedAccount(t *testing.T) {
	for index, appCode := range []string{"lalu", "yumi", "aslan"} {
		t.Run(appCode, func(t *testing.T) {
			order := ledger.ExternalRechargeOrder{
				OrderID:        "order-" + appCode,
				AppCode:        appCode,
				TargetUserID:   int64(81000 + index),
				USDMinorAmount: 30000,
				ProviderCode:   ledger.PaymentProviderUSDTTRC20,
				ReceiveAddress: "TReceiveAddress-" + appCode,
				Status:         ledger.ExternalRechargeStatusPending,
				CreatedAtMS:    1783344000000,
			}
			repository := &fakeH5USDTRepository{order: order}
			binance := &fakeBinanceClient{record: walletservice.BinanceTransferRecord{
				TxID:        "Off-chain transfer 390597460031",
				AmountMinor: 30000,
				Address:     order.ReceiveAddress,
				RawJSON:     `{"txId":"Off-chain transfer 390597460031","status":1}`,
			}}
			tron := &fakeTronUSDTClient{err: xerr.New(xerr.Internal, "tron should not be called")}
			svc := walletservice.New(repository)
			svc.SetBinanceClient(binance)
			svc.SetTronUSDTClient(tron)

			credited, err := svc.SubmitH5RechargeTx(context.Background(), ledger.SubmitExternalRechargeTxCommand{
				AppCode:      appCode,
				OrderID:      order.OrderID,
				TargetUserID: order.TargetUserID,
				TxHash:       "390597460031",
			})
			if err != nil {
				t.Fatalf("SubmitH5RechargeTx Binance off-chain failed: %v", err)
			}
			if credited.Status != ledger.ExternalRechargeStatusCredited {
				t.Fatalf("Binance off-chain order was not credited: %+v", credited)
			}
			if binance.calls != 1 || binance.req.AppCode != appCode || binance.req.ExternalID != "390597460031" || binance.req.Coin != ledger.PaidCurrencyUSDT || binance.req.Network != "TRX" || binance.req.ToAddress != order.ReceiveAddress || binance.req.AmountMinor != 30000 || binance.req.OrderTimeMS != order.CreatedAtMS {
				t.Fatalf("Binance app-scoped lookup mismatch: calls=%d req=%+v", binance.calls, binance.req)
			}
			if tron.lastTxHash != "" {
				t.Fatalf("Binance off-chain transfer must not query TronGrid: %+v", tron)
			}
			if repository.attachCommand.TxHash != "390597460031" || repository.attachPayload != binance.record.RawJSON {
				t.Fatalf("submitted Binance evidence was not attached: command=%+v payload=%s", repository.attachCommand, repository.attachPayload)
			}
			if repository.creditProviderOrderID != binance.record.TxID || repository.creditTxHash != "390597460031" || repository.creditPayload != binance.record.RawJSON {
				t.Fatalf("Binance credit evidence mismatch: provider_order_id=%s tx_hash=%s payload=%s", repository.creditProviderOrderID, repository.creditTxHash, repository.creditPayload)
			}
		})
	}
}

type fakeReceiptRepository struct {
	walletservice.Repository
	channels  []ledger.ThirdPartyPaymentChannel
	query     ledger.ListThirdPartyPaymentChannelsQuery
	queryErr  error
	querySeen bool
}

type fakeH5USDTRepository struct {
	walletservice.Repository
	order                 ledger.ExternalRechargeOrder
	attachCommand         ledger.SubmitExternalRechargeTxCommand
	attachPayload         string
	creditProviderOrderID string
	creditTxHash          string
	creditPayload         string
}

func (r *fakeH5USDTRepository) GetExternalRechargeOrder(_ context.Context, appCode string, orderID string) (ledger.ExternalRechargeOrder, error) {
	if appCode != r.order.AppCode || orderID != r.order.OrderID {
		return ledger.ExternalRechargeOrder{}, xerr.New(xerr.NotFound, "external recharge order not found")
	}
	return r.order, nil
}

func (r *fakeH5USDTRepository) AttachExternalRechargeTx(_ context.Context, command ledger.SubmitExternalRechargeTxCommand, payloadJSON string) (ledger.ExternalRechargeOrder, error) {
	// 测试仓储保留入账前的凭证快照，用于断言上游查单结果没有在 service 层丢失。
	r.attachCommand = command
	r.attachPayload = payloadJSON
	r.order.TxHash = command.TxHash
	r.order.ProviderPayloadJSON = payloadJSON
	return r.order, nil
}

func (r *fakeH5USDTRepository) CreditExternalRechargeOrder(_ context.Context, appCode string, orderID string, providerOrderID string, txHash string, payloadJSON string) (ledger.ExternalRechargeOrder, error) {
	if appCode != r.order.AppCode || orderID != r.order.OrderID {
		return ledger.ExternalRechargeOrder{}, xerr.New(xerr.NotFound, "external recharge order not found")
	}
	// 只有校验通过才会进入此方法；真实 MySQL 仓储仍负责唯一凭证和原子入账约束。
	r.creditProviderOrderID = providerOrderID
	r.creditTxHash = txHash
	r.creditPayload = payloadJSON
	r.order.ProviderOrderID = providerOrderID
	r.order.TxHash = txHash
	r.order.ProviderPayloadJSON = payloadJSON
	r.order.Status = ledger.ExternalRechargeStatusCredited
	return r.order, nil
}

func (r *fakeReceiptRepository) ListThirdPartyPaymentChannels(_ context.Context, query ledger.ListThirdPartyPaymentChannelsQuery) ([]ledger.ThirdPartyPaymentChannel, error) {
	r.query = query
	r.querySeen = true
	if r.queryErr != nil {
		return nil, r.queryErr
	}
	return r.channels, nil
}

func (r *fakeReceiptRepository) GetExternalRechargeOrder(context.Context, string, string) (ledger.ExternalRechargeOrder, error) {
	return ledger.ExternalRechargeOrder{}, xerr.New(xerr.NotFound, "external recharge order not found")
}

func sameUTCDate(left time.Time, right time.Time) bool {
	left = left.UTC()
	right = right.UTC()
	return left.Year() == right.Year() && left.YearDay() == right.YearDay()
}

type fakeBinanceClient struct {
	req    walletservice.BinanceTransferLookupRequest
	record walletservice.BinanceTransferRecord
	err    error
	calls  int
}

func (f *fakeBinanceClient) FindUSDTTransfer(_ context.Context, req walletservice.BinanceTransferLookupRequest) (walletservice.BinanceTransferRecord, error) {
	f.calls++
	f.req = req
	if f.err != nil {
		return walletservice.BinanceTransferRecord{}, f.err
	}
	return f.record, nil
}
