package sresponse

import (
	"testing"

	gatewayv1 "github.com/tech/sendico/pkg/proto/common/gateway/v1"
	moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
	endpointv1 "github.com/tech/sendico/pkg/proto/payments/endpoint/v1"
	orchestrationv2 "github.com/tech/sendico/pkg/proto/payments/orchestration/v2"
	quotationv2 "github.com/tech/sendico/pkg/proto/payments/quotation/v2"
	sharedv1 "github.com/tech/sendico/pkg/proto/payments/shared/v1"
	"github.com/tech/sendico/server/interface/api/srequest"
	"go.mongodb.org/mongo-driver/v2/bson"
)

func TestToUserVisibleOperationsFiltersByVisibility(t *testing.T) {
	steps := []*orchestrationv2.StepExecution{
		{
			StepRef:          "hidden",
			ReportVisibility: orchestrationv2.ReportVisibility_REPORT_VISIBILITY_HIDDEN,
		},
		{
			StepRef:          "user",
			StepCode:         "hop.4.card_payout.send",
			State:            orchestrationv2.StepExecutionState_STEP_EXECUTION_STATE_RUNNING,
			ReportVisibility: orchestrationv2.ReportVisibility_REPORT_VISIBILITY_USER,
		},
		{
			StepRef:          "unspecified",
			StepCode:         "hop.4.card_payout.observe",
			State:            orchestrationv2.StepExecutionState_STEP_EXECUTION_STATE_COMPLETED,
			ReportVisibility: orchestrationv2.ReportVisibility_REPORT_VISIBILITY_UNSPECIFIED,
		},
		{
			StepRef:          "backoffice",
			ReportVisibility: orchestrationv2.ReportVisibility_REPORT_VISIBILITY_BACKOFFICE,
		},
	}

	ops := toUserVisibleOperations(steps)
	if len(ops) != 2 {
		t.Fatalf("operations count mismatch: got=%d want=2", len(ops))
	}
	if got, want := ops[0].StepRef, "user"; got != want {
		t.Fatalf("first operation step_ref mismatch: got=%q want=%q", got, want)
	}
	if got, want := ops[1].StepRef, "unspecified"; got != want {
		t.Fatalf("second operation step_ref mismatch: got=%q want=%q", got, want)
	}
}

func TestToPaymentFailureUsesVisibleOperationsOnly(t *testing.T) {
	dto := toPayment(&orchestrationv2.Payment{
		PaymentRef: "pay-1",
		State:      orchestrationv2.OrchestrationState_ORCHESTRATION_STATE_FAILED,
		StepExecutions: []*orchestrationv2.StepExecution{
			{
				StepRef:          "hidden_failed",
				StepCode:         "edge.1_2.ledger.debit",
				State:            orchestrationv2.StepExecutionState_STEP_EXECUTION_STATE_FAILED,
				ReportVisibility: orchestrationv2.ReportVisibility_REPORT_VISIBILITY_HIDDEN,
				Failure: &orchestrationv2.Failure{
					Category: sharedv1.PaymentFailureCode_FAILURE_LEDGER,
					Message:  "internal hold release failure",
				},
			},
			{
				StepRef:          "user_failed",
				StepCode:         "hop.4.card_payout.send",
				State:            orchestrationv2.StepExecutionState_STEP_EXECUTION_STATE_FAILED,
				ReportVisibility: orchestrationv2.ReportVisibility_REPORT_VISIBILITY_USER,
				Failure: &orchestrationv2.Failure{
					Category: sharedv1.PaymentFailureCode_FAILURE_CHAIN,
					Message:  "card declined",
				},
			},
		},
	})
	if dto == nil {
		t.Fatal("expected non-nil payment dto")
	}
	if got, want := dto.FailureCode, "failure_chain"; got != want {
		t.Fatalf("failure_code mismatch: got=%q want=%q", got, want)
	}
	if got, want := dto.FailureReason, "card declined"; got != want {
		t.Fatalf("failure_reason mismatch: got=%q want=%q", got, want)
	}
	if len(dto.Operations) != 1 {
		t.Fatalf("operations count mismatch: got=%d want=1", len(dto.Operations))
	}
	if got, want := dto.Operations[0].StepRef, "user_failed"; got != want {
		t.Fatalf("visible operation mismatch: got=%q want=%q", got, want)
	}
}

func TestToPaymentIgnoresHiddenFailures(t *testing.T) {
	dto := toPayment(&orchestrationv2.Payment{
		PaymentRef: "pay-2",
		State:      orchestrationv2.OrchestrationState_ORCHESTRATION_STATE_FAILED,
		StepExecutions: []*orchestrationv2.StepExecution{
			{
				StepRef:          "hidden_failed",
				StepCode:         "edge.1_2.ledger.release",
				State:            orchestrationv2.StepExecutionState_STEP_EXECUTION_STATE_FAILED,
				ReportVisibility: orchestrationv2.ReportVisibility_REPORT_VISIBILITY_BACKOFFICE,
				Failure: &orchestrationv2.Failure{
					Category: sharedv1.PaymentFailureCode_FAILURE_LEDGER,
					Message:  "backoffice only failure",
				},
			},
		},
	})
	if dto == nil {
		t.Fatal("expected non-nil payment dto")
	}
	if got := dto.FailureCode; got != "" {
		t.Fatalf("expected empty failure_code, got=%q", got)
	}
	if got := dto.FailureReason; got != "" {
		t.Fatalf("expected empty failure_reason, got=%q", got)
	}
	if len(dto.Operations) != 0 {
		t.Fatalf("expected no visible operations, got=%d", len(dto.Operations))
	}
}

func TestToPaymentMapsIntentComment(t *testing.T) {
	dto := toPayment(&orchestrationv2.Payment{
		PaymentRef: "pay-3",
		State:      orchestrationv2.OrchestrationState_ORCHESTRATION_STATE_CREATED,
		IntentSnapshot: &quotationv2.QuoteIntent{
			Comment: "  invoice-7  ",
		},
	})
	if dto == nil {
		t.Fatal("expected non-nil payment dto")
	}
	if got, want := dto.Comment, "invoice-7"; got != want {
		t.Fatalf("comment mismatch: got=%q want=%q", got, want)
	}
}

func TestToPaymentMapsSourceAndDestination(t *testing.T) {
	sourceRaw, err := bson.Marshal(struct {
		WalletID string `bson:"walletId"`
	}{
		WalletID: "wallet-src-1",
	})
	if err != nil {
		t.Fatalf("marshal source method data: %v", err)
	}
	destinationRaw, err := bson.Marshal(struct {
		Currency       string  `bson:"currency"`
		Address        string  `bson:"address"`
		Network        string  `bson:"network"`
		DestinationTag *string `bson:"destinationTag,omitempty"`
	}{
		Currency: "USDT",
		Address:  "TXabc",
		Network:  "TRON_MAINNET",
	})
	if err != nil {
		t.Fatalf("marshal destination method data: %v", err)
	}

	dto := toPayment(&orchestrationv2.Payment{
		PaymentRef: "pay-src-dst",
		State:      orchestrationv2.OrchestrationState_ORCHESTRATION_STATE_CREATED,
		IntentSnapshot: &quotationv2.QuoteIntent{
			Source: &endpointv1.PaymentEndpoint{
				Source: &endpointv1.PaymentEndpoint_PaymentMethod{
					PaymentMethod: &endpointv1.PaymentMethod{
						Type: endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_WALLET,
						Data: sourceRaw,
					},
				},
			},
			Destination: &endpointv1.PaymentEndpoint{
				Source: &endpointv1.PaymentEndpoint_PaymentMethod{
					PaymentMethod: &endpointv1.PaymentMethod{
						Type: endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_CRYPTO_ADDRESS,
						Data: destinationRaw,
					},
				},
			},
		},
	})
	if dto == nil {
		t.Fatal("expected non-nil payment dto")
	}
	if dto.Source == nil {
		t.Fatal("expected source endpoint")
	}
	if got, want := dto.Source.Type, string(srequest.EndpointTypeWallet); got != want {
		t.Fatalf("source type mismatch: got=%q want=%q", got, want)
	}
	sourceEndpoint, ok := dto.Source.Data.(srequest.WalletEndpoint)
	if !ok {
		t.Fatalf("source endpoint payload type mismatch: got=%T", dto.Source.Data)
	}
	if got, want := sourceEndpoint.WalletID, "wallet-src-1"; got != want {
		t.Fatalf("source wallet id mismatch: got=%q want=%q", got, want)
	}
	if dto.Destination == nil {
		t.Fatal("expected destination endpoint")
	}
	if got, want := dto.Destination.Type, string(srequest.EndpointTypeExternalChain); got != want {
		t.Fatalf("destination type mismatch: got=%q want=%q", got, want)
	}
	destinationEndpoint, ok := dto.Destination.Data.(srequest.ExternalChainEndpoint)
	if !ok {
		t.Fatalf("destination endpoint payload type mismatch: got=%T", dto.Destination.Data)
	}
	if got, want := destinationEndpoint.Address, "TXabc"; got != want {
		t.Fatalf("destination address mismatch: got=%q want=%q", got, want)
	}
	if destinationEndpoint.Asset == nil {
		t.Fatal("expected destination asset")
	}
	if got, want := destinationEndpoint.Asset.TokenSymbol, "USDT"; got != want {
		t.Fatalf("destination token mismatch: got=%q want=%q", got, want)
	}
	if got, want := destinationEndpoint.Asset.Chain, srequest.ChainNetworkTronMainnet; got != want {
		t.Fatalf("destination chain mismatch: got=%q want=%q", got, want)
	}
}

func TestToPaymentMapsEndpointRefs(t *testing.T) {
	dto := toPayment(&orchestrationv2.Payment{
		PaymentRef: "pay-refs",
		State:      orchestrationv2.OrchestrationState_ORCHESTRATION_STATE_CREATED,
		IntentSnapshot: &quotationv2.QuoteIntent{
			Source: &endpointv1.PaymentEndpoint{
				Source: &endpointv1.PaymentEndpoint_PaymentMethodRef{
					PaymentMethodRef: "pm-123",
				},
			},
			Destination: &endpointv1.PaymentEndpoint{
				Source: &endpointv1.PaymentEndpoint_PayeeRef{
					PayeeRef: "payee-777",
				},
			},
		},
	})
	if dto == nil {
		t.Fatal("expected non-nil payment dto")
	}
	if dto.Source == nil {
		t.Fatal("expected source endpoint")
	}
	if got, want := dto.Source.PaymentMethodRef, "pm-123"; got != want {
		t.Fatalf("source payment_method_ref mismatch: got=%q want=%q", got, want)
	}
	if dto.Destination == nil {
		t.Fatal("expected destination endpoint")
	}
	if got, want := dto.Destination.PayeeRef, "payee-777"; got != want {
		t.Fatalf("destination payee_ref mismatch: got=%q want=%q", got, want)
	}
}

func TestToPaymentQuote_MapsIntentRef(t *testing.T) {
	dto := toPaymentQuote(&quotationv2.PaymentQuote{
		QuoteRef:  "quote-1",
		IntentRef: "intent-1",
	})
	if dto == nil {
		t.Fatal("expected non-nil quote dto")
	}
	if got, want := dto.QuoteRef, "quote-1"; got != want {
		t.Fatalf("quote_ref mismatch: got=%q want=%q", got, want)
	}
	if got, want := dto.IntentRef, "intent-1"; got != want {
		t.Fatalf("intent_ref mismatch: got=%q want=%q", got, want)
	}
}

func TestToPaymentOperation_MapsOperationRefAndGateway(t *testing.T) {
	op := toPaymentOperation(&orchestrationv2.StepExecution{
		StepRef:  "step-1",
		StepCode: "hop.4.card_payout.send",
		State:    orchestrationv2.StepExecutionState_STEP_EXECUTION_STATE_COMPLETED,
		Refs: []*orchestrationv2.ExternalReference{
			{
				Rail:              gatewayv1.Rail_RAIL_CARD,
				GatewayInstanceId: "mcards",
				Kind:              "operation_ref",
				Ref:               "op-123",
			},
		},
	})

	if got, want := op.OperationRef, "op-123"; got != want {
		t.Fatalf("operation_ref mismatch: got=%q want=%q", got, want)
	}
	if got, want := op.Gateway, "mcards_gateway"; got != want {
		t.Fatalf("gateway mismatch: got=%q want=%q", got, want)
	}
}

func TestToPaymentOperation_InfersGatewayFromStepCode(t *testing.T) {
	op := toPaymentOperation(&orchestrationv2.StepExecution{
		StepRef:  "step-2",
		StepCode: "edge.1_2.ledger.debit",
		State:    orchestrationv2.StepExecutionState_STEP_EXECUTION_STATE_COMPLETED,
	})

	if got := op.OperationRef; got != "" {
		t.Fatalf("expected empty operation_ref, got=%q", got)
	}
	if got, want := op.Gateway, "ledger"; got != want {
		t.Fatalf("gateway mismatch: got=%q want=%q", got, want)
	}
}

func TestToPaymentOperation_DoesNotFallbackToCardPayoutRef(t *testing.T) {
	op := toPaymentOperation(&orchestrationv2.StepExecution{
		StepRef:  "step-3",
		StepCode: "hop.4.card_payout.send",
		State:    orchestrationv2.StepExecutionState_STEP_EXECUTION_STATE_COMPLETED,
		Refs: []*orchestrationv2.ExternalReference{
			{
				Rail:              gatewayv1.Rail_RAIL_CARD,
				GatewayInstanceId: "mcards",
				Kind:              "card_payout_ref",
				Ref:               "payout-123",
			},
		},
	})

	if got := op.OperationRef; got != "" {
		t.Fatalf("expected empty operation_ref, got=%q", got)
	}
	if got, want := op.Gateway, "mcards_gateway"; got != want {
		t.Fatalf("gateway mismatch: got=%q want=%q", got, want)
	}
}

func TestToPaymentOperation_MapsAmount(t *testing.T) {
	op := toPaymentOperation(&orchestrationv2.StepExecution{
		StepRef:  "step-4",
		StepCode: "hop.4.card_payout.send",
		State:    orchestrationv2.StepExecutionState_STEP_EXECUTION_STATE_COMPLETED,
	})

	if got := op.Money; got != nil {
		t.Fatalf("expected nil money payload without step money, got=%+v", got)
	}
}

func TestToPaymentOperation_PrefersExecutedMoney(t *testing.T) {
	op := toPaymentOperation(&orchestrationv2.StepExecution{
		StepRef:  "step-4b",
		StepCode: "hop.4.card_payout.send",
		State:    orchestrationv2.StepExecutionState_STEP_EXECUTION_STATE_COMPLETED,
		Money: &orchestrationv2.StepExecutionMoney{
			Planned: &orchestrationv2.StepExecutionMoneySnapshot{
				Amount: &moneyv1.Money{Amount: "88.00", Currency: "EUR"},
			},
			Executed: &orchestrationv2.StepExecutionMoneySnapshot{
				Amount: &moneyv1.Money{Amount: "99.95", Currency: "EUR"},
			},
		},
	})

	if op.Money == nil || op.Money.Executed == nil || op.Money.Executed.Amount == nil {
		t.Fatal("expected executed amount to be mapped")
	}
	if got, want := op.Money.Executed.Amount.Amount, "99.95"; got != want {
		t.Fatalf("executed amount.value mismatch: got=%q want=%q", got, want)
	}
	if got, want := op.Money.Executed.Amount.Currency, "EUR"; got != want {
		t.Fatalf("executed amount.currency mismatch: got=%q want=%q", got, want)
	}
	if op.Money.Planned == nil || op.Money.Planned.Amount == nil {
		t.Fatal("expected planned amount to be exposed")
	}
	if got, want := op.Money.Planned.Amount.Amount, "88.00"; got != want {
		t.Fatalf("planned amount.value mismatch: got=%q want=%q", got, want)
	}
	if got, want := op.Money.Planned.Amount.Currency, "EUR"; got != want {
		t.Fatalf("planned amount.currency mismatch: got=%q want=%q", got, want)
	}
	if got := op.Money.Executed.ConvertedAmount; got != nil {
		t.Fatalf("expected no executed converted_amount for non-fx operation, got=%+v", got)
	}
}

func TestToPaymentOperation_UsesPlannedMoneyBeforeExecution(t *testing.T) {
	op := toPaymentOperation(&orchestrationv2.StepExecution{
		StepRef:  "step-4c",
		StepCode: "hop.4.card_payout.send",
		State:    orchestrationv2.StepExecutionState_STEP_EXECUTION_STATE_PENDING,
		Money: &orchestrationv2.StepExecutionMoney{
			Planned: &orchestrationv2.StepExecutionMoneySnapshot{
				Amount: &moneyv1.Money{Amount: "77.10", Currency: "USD"},
			},
		},
	})

	if op.Money == nil || op.Money.Planned == nil || op.Money.Planned.Amount == nil {
		t.Fatal("expected planned amount from structured planned money")
	}
	if got, want := op.Money.Planned.Amount.Amount, "77.10"; got != want {
		t.Fatalf("planned amount.value mismatch: got=%q want=%q", got, want)
	}
	if got, want := op.Money.Planned.Amount.Currency, "USD"; got != want {
		t.Fatalf("planned amount.currency mismatch: got=%q want=%q", got, want)
	}
	if got := op.Money.Executed; got != nil {
		t.Fatalf("expected no executed snapshot before execution, got=%+v", got)
	}
}

func TestToPaymentOperation_UsesStructuredMoneyEnvelope(t *testing.T) {
	op := toPaymentOperation(&orchestrationv2.StepExecution{
		StepRef:  "step-4d",
		StepCode: "hop.4.card_payout.send",
		State:    orchestrationv2.StepExecutionState_STEP_EXECUTION_STATE_PENDING,
		Money: &orchestrationv2.StepExecutionMoney{
			Planned: &orchestrationv2.StepExecutionMoneySnapshot{
				Amount: &moneyv1.Money{Amount: "66.00", Currency: "USD"},
			},
			Executed: &orchestrationv2.StepExecutionMoneySnapshot{
				Amount: &moneyv1.Money{Amount: "67.00", Currency: "USD"},
			},
		},
	})

	if op.Money == nil || op.Money.Executed == nil || op.Money.Executed.Amount == nil {
		t.Fatal("expected amount from structured executed money")
	}
	if got, want := op.Money.Executed.Amount.Amount, "67.00"; got != want {
		t.Fatalf("executed amount.value mismatch: got=%q want=%q", got, want)
	}
	if op.Money.Planned == nil || op.Money.Planned.Amount == nil {
		t.Fatal("expected planned amount from structured money")
	}
	if got, want := op.Money.Planned.Amount.Amount, "66.00"; got != want {
		t.Fatalf("planned amount.value mismatch: got=%q want=%q", got, want)
	}
}

func TestToPaymentOperation_MapsFxTwoAmounts(t *testing.T) {
	op := toPaymentOperation(&orchestrationv2.StepExecution{
		StepRef:  "step-5",
		StepCode: "hop.2.settlement.fx_convert",
		State:    orchestrationv2.StepExecutionState_STEP_EXECUTION_STATE_COMPLETED,
		Money: &orchestrationv2.StepExecutionMoney{
			Executed: &orchestrationv2.StepExecutionMoneySnapshot{
				ConvertedAmount: &moneyv1.Money{Amount: "100.00", Currency: "EUR"},
			},
		},
	})

	if op.Money == nil || op.Money.Executed == nil {
		t.Fatal("expected executed snapshot to be mapped")
	}
	if got := op.Money.Executed.Amount; got != nil {
		t.Fatalf("expected nil base amount without executed_money, got=%+v", got)
	}
	if op.Money.Executed.ConvertedAmount == nil {
		t.Fatal("expected fx converted amount to be mapped")
	}
	if got, want := op.Money.Executed.ConvertedAmount.Amount, "100.00"; got != want {
		t.Fatalf("converted amount.value mismatch: got=%q want=%q", got, want)
	}
	if got, want := op.Money.Executed.ConvertedAmount.Currency, "EUR"; got != want {
		t.Fatalf("converted amount.currency mismatch: got=%q want=%q", got, want)
	}
}

func TestToPaymentOperation_UsesPlannedFxAmountsBeforeExecution(t *testing.T) {
	op := toPaymentOperation(&orchestrationv2.StepExecution{
		StepRef:  "step-5b",
		StepCode: "hop.2.settlement.fx_convert",
		State:    orchestrationv2.StepExecutionState_STEP_EXECUTION_STATE_PENDING,
		Money: &orchestrationv2.StepExecutionMoney{
			Planned: &orchestrationv2.StepExecutionMoneySnapshot{
				Amount:          &moneyv1.Money{Amount: "109.50", Currency: "USDT"},
				ConvertedAmount: &moneyv1.Money{Amount: "100.00", Currency: "EUR"},
			},
		},
	})

	if op.Money == nil || op.Money.Planned == nil {
		t.Fatal("expected planned snapshot from structured money")
	}
	if got, want := op.Money.Planned.Amount.Amount, "109.50"; got != want {
		t.Fatalf("base amount.value mismatch: got=%q want=%q", got, want)
	}
	if got, want := op.Money.Planned.Amount.Currency, "USDT"; got != want {
		t.Fatalf("base amount.currency mismatch: got=%q want=%q", got, want)
	}
	if op.Money.Planned.ConvertedAmount == nil {
		t.Fatal("expected fx converted amount from structured planned money")
	}
	if got, want := op.Money.Planned.ConvertedAmount.Amount, "100.00"; got != want {
		t.Fatalf("converted amount.value mismatch: got=%q want=%q", got, want)
	}
	if got, want := op.Money.Planned.ConvertedAmount.Currency, "EUR"; got != want {
		t.Fatalf("converted amount.currency mismatch: got=%q want=%q", got, want)
	}
	if got := op.Money.Executed; got != nil {
		t.Fatalf("expected nil executed snapshot before execution, got=%+v", got)
	}
}

func TestToPaymentOperation_UsesStructuredFxMoneyEnvelope(t *testing.T) {
	op := toPaymentOperation(&orchestrationv2.StepExecution{
		StepRef:  "step-5c",
		StepCode: "hop.2.settlement.fx_convert",
		State:    orchestrationv2.StepExecutionState_STEP_EXECUTION_STATE_COMPLETED,
		Money: &orchestrationv2.StepExecutionMoney{
			Planned: &orchestrationv2.StepExecutionMoneySnapshot{
				Amount:          &moneyv1.Money{Amount: "109.50", Currency: "USDT"},
				ConvertedAmount: &moneyv1.Money{Amount: "100.00", Currency: "EUR"},
			},
			Executed: &orchestrationv2.StepExecutionMoneySnapshot{
				Amount:          &moneyv1.Money{Amount: "110.00", Currency: "USDT"},
				ConvertedAmount: &moneyv1.Money{Amount: "101.00", Currency: "EUR"},
			},
		},
	})

	if op.Money == nil || op.Money.Executed == nil || op.Money.Planned == nil {
		t.Fatal("expected snapshots from structured money")
	}
	if got, want := op.Money.Executed.Amount.Amount, "110.00"; got != want {
		t.Fatalf("amount.value mismatch: got=%q want=%q", got, want)
	}
	if got, want := op.Money.Executed.ConvertedAmount.Amount, "101.00"; got != want {
		t.Fatalf("converted amount.value mismatch: got=%q want=%q", got, want)
	}
	if op.Money.Planned.Amount == nil || op.Money.Planned.ConvertedAmount == nil {
		t.Fatal("expected planned amounts from structured money")
	}
	if got, want := op.Money.Planned.Amount.Amount, "109.50"; got != want {
		t.Fatalf("planned amount.value mismatch: got=%q want=%q", got, want)
	}
	if got, want := op.Money.Planned.ConvertedAmount.Amount, "100.00"; got != want {
		t.Fatalf("planned converted amount.value mismatch: got=%q want=%q", got, want)
	}
}

func TestToPaymentOperation_FxWithExecutedMoney_StillProvidesTwoAmounts(t *testing.T) {
	op := toPaymentOperation(&orchestrationv2.StepExecution{
		StepRef:  "step-6",
		StepCode: "hop.2.settlement.fx_convert",
		State:    orchestrationv2.StepExecutionState_STEP_EXECUTION_STATE_COMPLETED,
		Money: &orchestrationv2.StepExecutionMoney{
			Executed: &orchestrationv2.StepExecutionMoneySnapshot{
				Amount:          &moneyv1.Money{Amount: "109.50", Currency: "USDT"},
				ConvertedAmount: &moneyv1.Money{Amount: "100.00", Currency: "EUR"},
			},
		},
	})

	if op.Money == nil || op.Money.Executed == nil || op.Money.Executed.Amount == nil {
		t.Fatal("expected fx base amount to be mapped")
	}
	if got, want := op.Money.Executed.Amount.Amount, "109.50"; got != want {
		t.Fatalf("base amount.value mismatch: got=%q want=%q", got, want)
	}
	if got, want := op.Money.Executed.Amount.Currency, "USDT"; got != want {
		t.Fatalf("base amount.currency mismatch: got=%q want=%q", got, want)
	}
	if op.Money.Executed.ConvertedAmount == nil {
		t.Fatal("expected fx quote amount to be mapped")
	}
	if got, want := op.Money.Executed.ConvertedAmount.Amount, "100.00"; got != want {
		t.Fatalf("converted amount.value mismatch: got=%q want=%q", got, want)
	}
	if got, want := op.Money.Executed.ConvertedAmount.Currency, "EUR"; got != want {
		t.Fatalf("converted amount.currency mismatch: got=%q want=%q", got, want)
	}
}
