package quotation

import (
	"testing"

	fxv1 "github.com/tech/sendico/pkg/proto/common/fx/v1"
	moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
	quotationv2 "github.com/tech/sendico/pkg/proto/payments/quotation/v2"
	sharedv1 "github.com/tech/sendico/pkg/proto/payments/shared/v1"
)

func TestResolvedFeeTreatmentForQuote(t *testing.T) {
	cases := []struct {
		name   string
		intent *sharedv1.PaymentIntent
		want   quotationv2.FeeTreatment
	}{
		{
			name:   "nil intent defaults",
			intent: nil,
			want:   quotationv2.FeeTreatment_FEE_TREATMENT_ADD_TO_SOURCE,
		},
		{
			name:   "no attributes defaults",
			intent: &sharedv1.PaymentIntent{},
			want:   quotationv2.FeeTreatment_FEE_TREATMENT_ADD_TO_SOURCE,
		},
		{
			name: "snake key parsed",
			intent: &sharedv1.PaymentIntent{
				Attributes: map[string]string{
					"fee_treatment": "deduct_from_destination",
				},
			},
			want: quotationv2.FeeTreatment_FEE_TREATMENT_DEDUCT_FROM_DESTINATION,
		},
		{
			name: "camel key parsed",
			intent: &sharedv1.PaymentIntent{
				Attributes: map[string]string{
					"feeTreatment": "fee_treatment_deduct_from_destination",
				},
			},
			want: quotationv2.FeeTreatment_FEE_TREATMENT_DEDUCT_FROM_DESTINATION,
		},
		{
			name: "invalid value falls back to default",
			intent: &sharedv1.PaymentIntent{
				Attributes: map[string]string{
					"fee_treatment": "something_else",
				},
			},
			want: quotationv2.FeeTreatment_FEE_TREATMENT_ADD_TO_SOURCE,
		},
		{
			name: "snake key takes precedence over camel key",
			intent: &sharedv1.PaymentIntent{
				Attributes: map[string]string{
					"fee_treatment": "add_to_source",
					"feeTreatment":  "deduct_from_destination",
				},
			},
			want: quotationv2.FeeTreatment_FEE_TREATMENT_ADD_TO_SOURCE,
		},
		{
			name: "value is trimmed and case-insensitive",
			intent: &sharedv1.PaymentIntent{
				Attributes: map[string]string{
					"fee_treatment": "  FEE_TREATMENT_DEDUCT_FROM_DESTINATION  ",
				},
			},
			want: quotationv2.FeeTreatment_FEE_TREATMENT_DEDUCT_FROM_DESTINATION,
		},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			got := resolvedFeeTreatmentForQuote(tc.intent)
			if got != tc.want {
				t.Fatalf("unexpected fee treatment: got=%s want=%s", got.String(), tc.want.String())
			}
		})
	}
}

func TestShouldRequestFX_DoesNotDependOnRequiresFxFlag(t *testing.T) {
	intent := &sharedv1.PaymentIntent{
		Amount:             &moneyv1.Money{Amount: "10", Currency: "USDT"},
		SettlementCurrency: "USDT",
		RequiresFx:         true,
	}
	if got := shouldRequestFX(intent); got {
		t.Fatalf("expected shouldRequestFX=false when only requires_fx=true without FX data")
	}
}

func TestShouldRequestFX_UsesFXIntentOrCurrencyDifference(t *testing.T) {
	withPair := &sharedv1.PaymentIntent{
		Amount: &moneyv1.Money{Amount: "10", Currency: "USDT"},
		Fx: &sharedv1.FXIntent{
			Pair: &fxv1.CurrencyPair{Base: "USDT", Quote: "RUB"},
			Side: fxv1.Side_SELL_BASE_BUY_QUOTE,
		},
	}
	if got := shouldRequestFX(withPair); !got {
		t.Fatalf("expected shouldRequestFX=true for explicit fx intent")
	}

	withDerived := &sharedv1.PaymentIntent{
		Amount:             &moneyv1.Money{Amount: "10", Currency: "USDT"},
		SettlementCurrency: "RUB",
	}
	if got := shouldRequestFX(withDerived); !got {
		t.Fatalf("expected shouldRequestFX=true for derived FX from currency mismatch")
	}
}

func TestFXIntentForQuote_DefaultsUnspecifiedSideForExplicitPair(t *testing.T) {
	intent := &sharedv1.PaymentIntent{
		Fx: &sharedv1.FXIntent{
			Pair:              &fxv1.CurrencyPair{Base: "USDT", Quote: "RUB"},
			Side:              fxv1.Side_SIDE_UNSPECIFIED,
			Firm:              true,
			TtlMs:             5000,
			PreferredProvider: "provider-a",
			MaxAgeMs:          750,
		},
	}

	fx := fxIntentForQuote(intent)
	if fx == nil {
		t.Fatal("expected fx intent")
	}
	if fx.GetSide() != fxv1.Side_SELL_BASE_BUY_QUOTE {
		t.Fatalf("unexpected side: got=%s", fx.GetSide().String())
	}
	if fx.GetPair() == nil || fx.GetPair().GetBase() != "USDT" || fx.GetPair().GetQuote() != "RUB" {
		t.Fatalf("unexpected pair: %+v", fx.GetPair())
	}
	if !fx.GetFirm() || fx.GetTtlMs() != 5000 || fx.GetPreferredProvider() != "provider-a" || fx.GetMaxAgeMs() != 750 {
		t.Fatalf("unexpected fx options: %+v", fx)
	}
}
