package quote_computation_service

import (
	"context"
	"errors"
	"github.com/tech/sendico/pkg/discovery"
	"strings"
	"testing"

	"github.com/tech/sendico/payments/quotation/internal/service/quotation/transfer_intent_hydrator"
	"github.com/tech/sendico/payments/storage/model"
	"github.com/tech/sendico/pkg/merrors"
	"go.mongodb.org/mongo-driver/v2/bson"
)

func TestBuildPlan_UsesRouteGraphPath(t *testing.T) {
	svc := New(nil,
		WithRouteStore(staticRouteStore{items: []*model.PaymentRoute{
			{FromRail: discovery.RailCrypto, ToRail: discovery.RailProviderSettlement, Network: "TRON", IsEnabled: true},
			{FromRail: discovery.RailProviderSettlement, ToRail: discovery.RailCardPayout, Network: "TRON", IsEnabled: true},
		}}),
		WithGatewayRegistry(staticGatewayRegistry{
			items: []*model.GatewayInstanceDescriptor{
				{
					ID:         "crypto-gw",
					InstanceID: "crypto-gw",
					Rail:       discovery.RailCrypto,
					Network:    "TRON",
					Currencies: []string{"USDT"},
					Capabilities: model.RailCapabilities{
						CanPayOut: true,
					},
					IsEnabled: true,
				},
				{
					ID:         "provider-gw",
					InstanceID: "provider-gw",
					Rail:       discovery.RailProviderSettlement,
					Network:    "TRON",
					Currencies: []string{"USDT"},
					IsEnabled:  true,
				},
				{
					ID:         "card-gw",
					InstanceID: "card-gw",
					Rail:       discovery.RailCardPayout,
					Currencies: []string{"USDT"},
					Capabilities: model.RailCapabilities{
						CanPayOut: true,
					},
					IsEnabled: true,
				},
			},
		}),
	)

	orgID := bson.NewObjectID()
	planModel, err := svc.BuildPlan(context.Background(), ComputeInput{
		OrganizationRef:    orgID.Hex(),
		OrganizationID:     orgID,
		BaseIdempotencyKey: "idem-graph",
		Intents:            []*transfer_intent_hydrator.QuoteIntent{sampleCryptoToCardQuoteIntent()},
	})
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if planModel == nil || len(planModel.Items) != 1 || planModel.Items[0] == nil {
		t.Fatalf("expected one plan item")
	}

	item := planModel.Items[0]
	if got, want := len(item.Steps), 3; got != want {
		t.Fatalf("unexpected step count: got=%d want=%d", got, want)
	}
	if got, want := string(item.Steps[1].Rail), string(discovery.RailProviderSettlement); got != want {
		t.Fatalf("unexpected transit rail: got=%q want=%q", got, want)
	}
	if got := strings.ToUpper(strings.TrimSpace(item.Route.GetHops()[1].GetRail())); got != "SETTLEMENT" {
		t.Fatalf("unexpected route transit hop rail: %q", got)
	}
}

func TestBuildPlan_RouteGraphNoPathReturnsError(t *testing.T) {
	svc := New(nil, WithRouteStore(staticRouteStore{items: []*model.PaymentRoute{
		{FromRail: discovery.RailCrypto, ToRail: discovery.RailLedger, Network: "TRON", IsEnabled: true},
	}}))

	orgID := bson.NewObjectID()
	_, err := svc.BuildPlan(context.Background(), ComputeInput{
		OrganizationRef:    orgID.Hex(),
		OrganizationID:     orgID,
		BaseIdempotencyKey: "idem-graph-no-path",
		Intents:            []*transfer_intent_hydrator.QuoteIntent{sampleCryptoToCardQuoteIntent()},
	})
	if !errors.Is(err, merrors.ErrInvalidArg) {
		t.Fatalf("expected invalid argument when no path exists in route graph, got %v", err)
	}
}

func TestBuildPlan_RouteGraphPrefersDirectPath(t *testing.T) {
	svc := New(nil,
		WithRouteStore(staticRouteStore{items: []*model.PaymentRoute{
			{FromRail: discovery.RailCrypto, ToRail: discovery.RailCardPayout, Network: "TRON", IsEnabled: true},
			{FromRail: discovery.RailCrypto, ToRail: discovery.RailLedger, Network: "TRON", IsEnabled: true},
			{FromRail: discovery.RailLedger, ToRail: discovery.RailCardPayout, Network: "TRON", IsEnabled: true},
		}}),
		WithGatewayRegistry(staticGatewayRegistry{
			items: []*model.GatewayInstanceDescriptor{
				{
					ID:         "crypto-gw",
					InstanceID: "crypto-gw",
					Rail:       discovery.RailCrypto,
					Network:    "TRON",
					Currencies: []string{"USDT"},
					Capabilities: model.RailCapabilities{
						CanPayOut: true,
					},
					IsEnabled: true,
				},
				{
					ID:         "card-gw",
					InstanceID: "card-gw",
					Rail:       discovery.RailCardPayout,
					Currencies: []string{"USDT"},
					Capabilities: model.RailCapabilities{
						CanPayOut: true,
					},
					IsEnabled: true,
				},
			},
		}),
	)

	orgID := bson.NewObjectID()
	planModel, err := svc.BuildPlan(context.Background(), ComputeInput{
		OrganizationRef:    orgID.Hex(),
		OrganizationID:     orgID,
		BaseIdempotencyKey: "idem-graph-direct",
		Intents:            []*transfer_intent_hydrator.QuoteIntent{sampleCryptoToCardQuoteIntent()},
	})
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if planModel == nil || len(planModel.Items) != 1 || planModel.Items[0] == nil {
		t.Fatalf("expected one plan item")
	}
	if got, want := len(planModel.Items[0].Steps), 2; got != want {
		t.Fatalf("expected direct two-step path, got %d steps", got)
	}
}

type staticRouteStore struct {
	items []*model.PaymentRoute
}

func (s staticRouteStore) List(context.Context, *model.PaymentRouteFilter) (*model.PaymentRouteList, error) {
	out := make([]*model.PaymentRoute, 0, len(s.items))
	for _, item := range s.items {
		if item == nil {
			continue
		}
		cloned := *item
		out = append(out, &cloned)
	}
	return &model.PaymentRouteList{Items: out}, nil
}
