package orchestrator

import (
	"testing"

	paymenttypes "github.com/tech/sendico/pkg/payments/types"
	feesv1 "github.com/tech/sendico/pkg/proto/billing/fees/v1"
	accountingv1 "github.com/tech/sendico/pkg/proto/common/accounting/v1"
	fxv1 "github.com/tech/sendico/pkg/proto/common/fx/v1"
	moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
	chainv1 "github.com/tech/sendico/pkg/proto/gateway/chain/v1"
	oraclev1 "github.com/tech/sendico/pkg/proto/oracle/v1"
)

func TestMoneyConversionRoundTrip(t *testing.T) {
	proto := &moneyv1.Money{Currency: "USD", Amount: "12.34"}
	model := moneyFromProto(proto)
	if model == nil || model.Currency != "USD" || model.Amount != "12.34" {
		t.Fatalf("moneyFromProto mismatch: %#v", model)
	}
	back := protoMoney(model)
	if back == nil || back.GetCurrency() != "USD" || back.GetAmount() != "12.34" {
		t.Fatalf("protoMoney mismatch: %#v", back)
	}
}

func TestFeePolicyConversionRoundTrip(t *testing.T) {
	proto := &feesv1.PolicyOverrides{InsufficientNet: feesv1.InsufficientNetPolicy_SWEEP_ORG_CASH}
	model := feePolicyFromProto(proto)
	if model == nil || model.InsufficientNet != paymenttypes.InsufficientNetSweepOrgCash {
		t.Fatalf("feePolicyFromProto mismatch: %#v", model)
	}
	back := feePolicyToProto(model)
	if back == nil || back.GetInsufficientNet() != feesv1.InsufficientNetPolicy_SWEEP_ORG_CASH {
		t.Fatalf("feePolicyToProto mismatch: %#v", back)
	}
}

func TestFeeLineConversionRoundTrip(t *testing.T) {
	protoLine := &feesv1.DerivedPostingLine{
		LedgerAccountRef: "ledger:fees",
		Money:            &moneyv1.Money{Currency: "EUR", Amount: "1.00"},
		LineType:         accountingv1.PostingLineType_POSTING_LINE_FEE,
		Side:             accountingv1.EntrySide_ENTRY_SIDE_DEBIT,
		Meta:             map[string]string{"k": "v"},
	}
	modelLines := feeLinesFromProto([]*feesv1.DerivedPostingLine{protoLine})
	if len(modelLines) != 1 {
		t.Fatalf("expected 1 model line, got %d", len(modelLines))
	}
	modelLine := modelLines[0]
	if modelLine.LedgerAccountRef != "ledger:fees" || modelLine.Money.GetCurrency() != "EUR" || modelLine.Money.GetAmount() != "1.00" {
		t.Fatalf("model line mismatch: %#v", modelLine)
	}
	if modelLine.LineType != paymenttypes.PostingLineTypeFee || modelLine.Side != paymenttypes.EntrySideDebit {
		t.Fatalf("model line enums mismatch: %#v", modelLine)
	}
	back := feeLinesToProto(modelLines)
	if len(back) != 1 {
		t.Fatalf("expected 1 proto line, got %d", len(back))
	}
	protoBack := back[0]
	if protoBack.GetLedgerAccountRef() != "ledger:fees" || protoBack.GetMoney().GetCurrency() != "EUR" || protoBack.GetMoney().GetAmount() != "1.00" {
		t.Fatalf("proto line mismatch: %#v", protoBack)
	}
	if protoBack.GetLineType() != accountingv1.PostingLineType_POSTING_LINE_FEE || protoBack.GetSide() != accountingv1.EntrySide_ENTRY_SIDE_DEBIT {
		t.Fatalf("proto line enums mismatch: %#v", protoBack)
	}
}

func TestFXQuoteConversionRoundTrip(t *testing.T) {
	proto := &oraclev1.Quote{
		QuoteRef:        "q1",
		Pair:            &fxv1.CurrencyPair{Base: "USD", Quote: "EUR"},
		Side:            fxv1.Side_SELL_BASE_BUY_QUOTE,
		Price:           &moneyv1.Decimal{Value: "0.9"},
		BaseAmount:      &moneyv1.Money{Currency: "USD", Amount: "100"},
		QuoteAmount:     &moneyv1.Money{Currency: "EUR", Amount: "90"},
		ExpiresAtUnixMs: 1700000000000,
		Provider:        "provider",
		RateRef:         "rate",
		Firm:            true,
	}
	model := fxQuoteFromProto(proto)
	if model == nil || model.QuoteRef != "q1" || model.Pair.GetBase() != "USD" || model.Pair.GetQuote() != "EUR" {
		t.Fatalf("fxQuoteFromProto mismatch: %#v", model)
	}
	if model.Side != paymenttypes.FXSideSellBaseBuyQuote || model.Price.GetValue() != "0.9" {
		t.Fatalf("fxQuoteFromProto enums mismatch: %#v", model)
	}
	back := fxQuoteToProto(model)
	if back == nil || back.GetQuoteRef() != "q1" || back.GetPair().GetBase() != "USD" || back.GetPair().GetQuote() != "EUR" {
		t.Fatalf("fxQuoteToProto mismatch: %#v", back)
	}
	if back.GetSide() != fxv1.Side_SELL_BASE_BUY_QUOTE || back.GetPrice().GetValue() != "0.9" {
		t.Fatalf("fxQuoteToProto enums mismatch: %#v", back)
	}
}

func TestAssetConversionRoundTrip(t *testing.T) {
	proto := &chainv1.Asset{
		Chain:           chainv1.ChainNetwork_CHAIN_NETWORK_TRON_MAINNET,
		TokenSymbol:     "USDT",
		ContractAddress: "0xabc",
	}
	model := assetFromProto(proto)
	if model == nil || model.Chain != "TRON" || model.TokenSymbol != "USDT" || model.ContractAddress != "0xabc" {
		t.Fatalf("assetFromProto mismatch: %#v", model)
	}
	back := assetToProto(model)
	if back == nil || back.GetChain() != chainv1.ChainNetwork_CHAIN_NETWORK_TRON_MAINNET || back.GetTokenSymbol() != "USDT" || back.GetContractAddress() != "0xabc" {
		t.Fatalf("assetToProto mismatch: %#v", back)
	}
}
