package opagg

import (
	"testing"
)

func TestAggregate_GroupsCompatibleItemsByRecipient(t *testing.T) {
	aggregator := New()

	in := Input{
		Items: []Item{
			{
				IntentSnapshot: sampleIntent("intent-a", "card-1", "100"),
				QuoteSnapshot:  sampleQuote("quote-batch", "100", "9150", "1.8"),
			},
			{
				IntentSnapshot: sampleIntent("intent-b", "card-1", "125"),
				QuoteSnapshot:  sampleQuote("quote-batch", "125", "11437.5", "1.8"),
			},
		},
	}

	out, err := aggregator.Aggregate(in)
	if err != nil {
		t.Fatalf("Aggregate returned error: %v", err)
	}
	if out == nil {
		t.Fatal("expected output")
	}
	if got, want := len(out.Groups), 1; got != want {
		t.Fatalf("groups count mismatch: got=%d want=%d", got, want)
	}

	group := out.Groups[0]
	if got, want := len(group.IntentRefs), 2; got != want {
		t.Fatalf("intent_refs count mismatch: got=%d want=%d", got, want)
	}
	if got, want := group.IntentRefs[0], "intent-a"; got != want {
		t.Fatalf("intent_refs[0] mismatch: got=%q want=%q", got, want)
	}
	if got, want := group.IntentRefs[1], "intent-b"; got != want {
		t.Fatalf("intent_refs[1] mismatch: got=%q want=%q", got, want)
	}
	if got, want := group.IntentSnapshot.Amount.Amount, "225"; got != want {
		t.Fatalf("intent amount mismatch: got=%q want=%q", got, want)
	}
	if group.QuoteSnapshot == nil {
		t.Fatal("expected quote snapshot")
	}
	if got, want := group.QuoteSnapshot.DebitAmount.Amount, "225"; got != want {
		t.Fatalf("debit amount mismatch: got=%q want=%q", got, want)
	}
	if got, want := group.QuoteSnapshot.ExpectedSettlementAmount.Amount, "20587.5"; got != want {
		t.Fatalf("settlement amount mismatch: got=%q want=%q", got, want)
	}
	if got, want := group.QuoteSnapshot.ExpectedFeeTotal.Amount, "3.6"; got != want {
		t.Fatalf("fee total mismatch: got=%q want=%q", got, want)
	}
	if got, want := group.QuoteSnapshot.TotalCost.Amount, "228.6"; got != want {
		t.Fatalf("total cost mismatch: got=%q want=%q", got, want)
	}
	if got, want := len(group.QuoteSnapshot.FeeLines), 2; got != want {
		t.Fatalf("fee lines mismatch: got=%d want=%d", got, want)
	}
	if got, want := group.QuoteSnapshot.FeeLines[0].Money.Amount, "3"; got != want {
		t.Fatalf("platform fee mismatch: got=%q want=%q", got, want)
	}
	if got, want := group.QuoteSnapshot.FeeLines[1].Money.Amount, "0.6"; got != want {
		t.Fatalf("tax fee mismatch: got=%q want=%q", got, want)
	}
	if group.IntentSnapshot.Attributes[attrAggregatedByRecipient] != "true" {
		t.Fatalf("expected aggregated attribute %q=true", attrAggregatedByRecipient)
	}
	if got, want := group.IntentSnapshot.Attributes[attrAggregatedItems], "2"; got != want {
		t.Fatalf("aggregated items mismatch: got=%q want=%q", got, want)
	}
}

func TestAggregate_DoesNotMergeDifferentRecipients(t *testing.T) {
	aggregator := New()

	in := Input{
		Items: []Item{
			{
				IntentSnapshot: sampleIntent("intent-a", "card-1", "100"),
				QuoteSnapshot:  sampleQuote("quote-batch", "100", "9150", "1.8"),
			},
			{
				IntentSnapshot: sampleIntent("intent-b", "card-2", "125"),
				QuoteSnapshot:  sampleQuote("quote-batch", "125", "11437.5", "1.8"),
			},
		},
	}

	out, err := aggregator.Aggregate(in)
	if err != nil {
		t.Fatalf("Aggregate returned error: %v", err)
	}
	if out == nil {
		t.Fatal("expected output")
	}
	if got, want := len(out.Groups), 2; got != want {
		t.Fatalf("groups count mismatch: got=%d want=%d", got, want)
	}
}

func TestAggregate_DoesNotMergeWhenBatchingIneligible(t *testing.T) {
	aggregator := New()

	first := sampleQuote("quote-batch", "100", "9150", "1.8")
	first.ExecutionConditions.BatchingEligible = false
	second := sampleQuote("quote-batch", "125", "11437.5", "1.8")
	second.ExecutionConditions.BatchingEligible = false

	in := Input{
		Items: []Item{
			{
				IntentSnapshot: sampleIntent("intent-a", "card-1", "100"),
				QuoteSnapshot:  first,
			},
			{
				IntentSnapshot: sampleIntent("intent-b", "card-1", "125"),
				QuoteSnapshot:  second,
			},
		},
	}

	out, err := aggregator.Aggregate(in)
	if err != nil {
		t.Fatalf("Aggregate returned error: %v", err)
	}
	if out == nil {
		t.Fatal("expected output")
	}
	if got, want := len(out.Groups), 2; got != want {
		t.Fatalf("groups count mismatch: got=%d want=%d", got, want)
	}
}

func TestAggregate_UserBatchQuoteSampleCompactsToSingleRecipientOperation(t *testing.T) {
	aggregator := New()

	in := Input{
		Items: []Item{
			{
				IntentSnapshot: sampleIntent("q-intent-1771599670962253000", "card-1", "100"),
				QuoteSnapshot:  sampleQuote("quote-batch-usdt-rub", "100", "9150", "1.8"),
			},
			{
				IntentSnapshot: sampleIntent("q-intent-1771599670962255000", "card-1", "125"),
				QuoteSnapshot:  sampleQuote("quote-batch-usdt-rub", "125", "11437.5", "1.8"),
			},
			{
				IntentSnapshot: sampleIntent("q-intent-1771599670962256000", "card-1", "80"),
				QuoteSnapshot:  sampleQuote("quote-batch-usdt-rub", "80", "7320", "1.8"),
			},
		},
	}

	out, err := aggregator.Aggregate(in)
	if err != nil {
		t.Fatalf("Aggregate returned error: %v", err)
	}
	if out == nil {
		t.Fatal("expected output")
	}
	if got, want := len(out.Groups), 1; got != want {
		t.Fatalf("groups count mismatch: got=%d want=%d", got, want)
	}

	group := out.Groups[0]
	if got, want := len(group.IntentRefs), 3; got != want {
		t.Fatalf("intent_refs count mismatch: got=%d want=%d", got, want)
	}
	if got, want := group.IntentSnapshot.Amount.Amount, "305"; got != want {
		t.Fatalf("intent amount mismatch: got=%q want=%q", got, want)
	}
	if group.QuoteSnapshot == nil {
		t.Fatal("expected quote snapshot")
	}
	if got, want := group.QuoteSnapshot.DebitAmount.Amount, "305"; got != want {
		t.Fatalf("debit amount mismatch: got=%q want=%q", got, want)
	}
	if got, want := group.QuoteSnapshot.ExpectedSettlementAmount.Amount, "27907.5"; got != want {
		t.Fatalf("settlement amount mismatch: got=%q want=%q", got, want)
	}
	if got, want := group.QuoteSnapshot.ExpectedFeeTotal.Amount, "5.4"; got != want {
		t.Fatalf("fee total mismatch: got=%q want=%q", got, want)
	}
	if got, want := group.QuoteSnapshot.TotalCost.Amount, "310.4"; got != want {
		t.Fatalf("total cost mismatch: got=%q want=%q", got, want)
	}
	if group.IntentSnapshot.Attributes[attrAggregatedByRecipient] != "true" {
		t.Fatalf("expected aggregated attribute %q=true", attrAggregatedByRecipient)
	}
	if got, want := group.IntentSnapshot.Attributes[attrAggregatedItems], "3"; got != want {
		t.Fatalf("aggregated items mismatch: got=%q want=%q", got, want)
	}
}

func TestAggregate_MergeByRecipient_IgnoresSourceAndQuoteRefs(t *testing.T) {
	aggregator := New()

	firstIntent := sampleIntent("intent-a", "card-1", "100")
	secondIntent := sampleIntent("intent-b", "card-1", "125")
	secondIntent.Source.ManagedWallet.ManagedWalletRef = "src-wallet-2"

	firstQuote := sampleQuote("quote-a", "100", "9150", "1.8")
	secondQuote := sampleQuote("quote-b", "125", "11437.5", "1.8")
	secondQuote.Route.RouteRef = "route-b"
	secondQuote.FXQuote.QuoteRef = "fx-b"
	secondQuote.FXQuote.RateRef = "rate-b"
	secondQuote.FXQuote.Price.Value = "92.7"

	in := Input{
		Items: []Item{
			{
				IntentSnapshot: firstIntent,
				QuoteSnapshot:  firstQuote,
				MergeMode:      MergeModeByRecipient,
			},
			{
				IntentSnapshot: secondIntent,
				QuoteSnapshot:  secondQuote,
				MergeMode:      MergeModeByRecipient,
			},
		},
	}

	out, err := aggregator.Aggregate(in)
	if err != nil {
		t.Fatalf("Aggregate returned error: %v", err)
	}
	if out == nil {
		t.Fatal("expected output")
	}
	if got, want := len(out.Groups), 1; got != want {
		t.Fatalf("groups count mismatch: got=%d want=%d", got, want)
	}
	group := out.Groups[0]
	if got, want := group.IntentSnapshot.Amount.Amount, "225"; got != want {
		t.Fatalf("intent amount mismatch: got=%q want=%q", got, want)
	}
	if group.QuoteSnapshot == nil {
		t.Fatal("expected quote snapshot")
	}
	if got, want := group.QuoteSnapshot.DebitAmount.Amount, "225"; got != want {
		t.Fatalf("debit amount mismatch: got=%q want=%q", got, want)
	}
	if got, want := group.QuoteSnapshot.ExpectedSettlementAmount.Amount, "20587.5"; got != want {
		t.Fatalf("settlement amount mismatch: got=%q want=%q", got, want)
	}
}
