package quote_idempotency_service

import (
	"testing"

	moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
	paymentv1 "github.com/tech/sendico/pkg/proto/common/payment/v1"
	endpointv1 "github.com/tech/sendico/pkg/proto/payments/endpoint/v1"
	quotationv2 "github.com/tech/sendico/pkg/proto/payments/quotation/v2"
	sharedv1 "github.com/tech/sendico/pkg/proto/payments/shared/v1"
	"go.mongodb.org/mongo-driver/v2/bson"
	"google.golang.org/protobuf/proto"
)

func TestFingerprintQuotePayment_IgnoresTransportFields(t *testing.T) {
	svc := New()
	base := &quotationv2.QuotePaymentRequest{
		Meta:           &sharedv1.RequestMeta{OrganizationRef: bson.NewObjectID().Hex()},
		IdempotencyKey: "idem-a",
		Intent:         testQuoteIntent("10"),
		PreviewOnly:    false,
		InitiatorRef:   "actor-1",
	}
	alt := proto.Clone(base).(*quotationv2.QuotePaymentRequest)
	alt.Meta = &sharedv1.RequestMeta{OrganizationRef: bson.NewObjectID().Hex()}
	alt.IdempotencyKey = "idem-b"
	alt.PreviewOnly = true

	if got, want := svc.FingerprintQuotePayment(base), svc.FingerprintQuotePayment(alt); got != want {
		t.Fatalf("expected same fingerprint, got %q != %q", got, want)
	}
}

func TestFingerprintQuotePayment_DetectsBusinessPayloadChanges(t *testing.T) {
	svc := New()
	base := &quotationv2.QuotePaymentRequest{
		Meta:           &sharedv1.RequestMeta{OrganizationRef: bson.NewObjectID().Hex()},
		IdempotencyKey: "idem-a",
		Intent:         testQuoteIntent("10"),
		InitiatorRef:   "actor-1",
	}

	changedInitiator := proto.Clone(base).(*quotationv2.QuotePaymentRequest)
	changedInitiator.InitiatorRef = "actor-2"
	if got, want := svc.FingerprintQuotePayment(base), svc.FingerprintQuotePayment(changedInitiator); got == want {
		t.Fatalf("expected different fingerprint for initiator change")
	}

	changedAmount := proto.Clone(base).(*quotationv2.QuotePaymentRequest)
	changedAmount.Intent = testQuoteIntent("11")
	if got, want := svc.FingerprintQuotePayment(base), svc.FingerprintQuotePayment(changedAmount); got == want {
		t.Fatalf("expected different fingerprint for amount change")
	}
}

func TestFingerprintQuotePayments_IgnoresTransportFields(t *testing.T) {
	svc := New()
	base := &quotationv2.QuotePaymentsRequest{
		Meta:           &sharedv1.RequestMeta{OrganizationRef: bson.NewObjectID().Hex()},
		IdempotencyKey: "idem-a",
		Intents: []*quotationv2.QuoteIntent{
			testQuoteIntent("10"),
			testQuoteIntent("20"),
		},
		PreviewOnly:  false,
		InitiatorRef: "actor-1",
	}
	alt := proto.Clone(base).(*quotationv2.QuotePaymentsRequest)
	alt.Meta = &sharedv1.RequestMeta{OrganizationRef: bson.NewObjectID().Hex()}
	alt.IdempotencyKey = "idem-b"
	alt.PreviewOnly = true

	if got, want := svc.FingerprintQuotePayments(base), svc.FingerprintQuotePayments(alt); got != want {
		t.Fatalf("expected same fingerprint, got %q != %q", got, want)
	}
}

func TestFingerprintQuotePayments_DetectsBusinessPayloadChanges(t *testing.T) {
	svc := New()
	base := &quotationv2.QuotePaymentsRequest{
		Meta:           &sharedv1.RequestMeta{OrganizationRef: bson.NewObjectID().Hex()},
		IdempotencyKey: "idem-a",
		Intents: []*quotationv2.QuoteIntent{
			testQuoteIntent("10"),
			testQuoteIntent("20"),
		},
		InitiatorRef: "actor-1",
	}

	changedInitiator := proto.Clone(base).(*quotationv2.QuotePaymentsRequest)
	changedInitiator.InitiatorRef = "actor-2"
	if got, want := svc.FingerprintQuotePayments(base), svc.FingerprintQuotePayments(changedInitiator); got == want {
		t.Fatalf("expected different fingerprint for initiator change")
	}

	reordered := proto.Clone(base).(*quotationv2.QuotePaymentsRequest)
	reordered.Intents = []*quotationv2.QuoteIntent{
		testQuoteIntent("20"),
		testQuoteIntent("10"),
	}
	if got, want := svc.FingerprintQuotePayments(base), svc.FingerprintQuotePayments(reordered); got == want {
		t.Fatalf("expected different fingerprint for intent order change")
	}
}

func testQuoteIntent(amount string) *quotationv2.QuoteIntent {
	return &quotationv2.QuoteIntent{
		Source:         endpointWithMethodRef("pm-src"),
		Destination:    endpointWithMethodRef("pm-dst"),
		Amount:         &moneyv1.Money{Amount: amount, Currency: "USD"},
		SettlementMode: paymentv1.SettlementMode_SETTLEMENT_FIX_SOURCE,
	}
}

func endpointWithMethodRef(methodRef string) *endpointv1.PaymentEndpoint {
	return &endpointv1.PaymentEndpoint{
		Source: &endpointv1.PaymentEndpoint_PaymentMethodRef{
			PaymentMethodRef: methodRef,
		},
	}
}
