package orchestrator

import (
	"strings"

	"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/psvc"
	"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/qsnap"
	"github.com/tech/sendico/payments/storage/model"
	"github.com/tech/sendico/pkg/discovery"
	paymenttypes "github.com/tech/sendico/pkg/payments/types"
)

type orchestratorBatchMergeKeyResolver struct {
	cardGatewayRoutes map[string]CardGatewayRoute
}

func newOrchestratorBatchMergeKeyResolver(routes map[string]CardGatewayRoute) psvc.BatchOptimizationMergeKeyResolver {
	return &orchestratorBatchMergeKeyResolver{
		cardGatewayRoutes: cloneCardGatewayRoutes(routes),
	}
}

func (r *orchestratorBatchMergeKeyResolver) ResolveMergeKey(
	item qsnap.ResolvedItem,
	selection psvc.BatchOptimizationSelection,
) (string, bool) {
	if psvc.BatchOptimizationGrouping(strings.TrimSpace(string(selection.GroupBy))) != psvc.BatchOptimizationGroupingRailTarget {
		return "", false
	}
	if model.ParseRail(string(selection.MatchRail)) != discovery.RailCrypto {
		return "", false
	}
	return r.cryptoRailTargetKey(item)
}

func (r *orchestratorBatchMergeKeyResolver) cryptoRailTargetKey(item qsnap.ResolvedItem) (string, bool) {
	destination := item.IntentSnapshot.Destination
	if destination.Type == model.EndpointTypeExternalChain && destination.ExternalChain != nil {
		return strings.Join([]string{
			"type=external_chain",
			"address=" + strings.TrimSpace(destination.ExternalChain.Address),
			"memo=" + strings.TrimSpace(destination.ExternalChain.Memo),
		}, "|"), true
	}
	if destination.Type == model.EndpointTypeManagedWallet && destination.ManagedWallet != nil {
		return strings.Join([]string{
			"type=managed_wallet",
			"wallet=" + strings.TrimSpace(destination.ManagedWallet.ManagedWalletRef),
		}, "|"), true
	}
	if destination.Type == model.EndpointTypeCard {
		address := r.resolveCardFundingAddress(item.QuoteSnapshot)
		if strings.TrimSpace(address) == "" {
			return "", false
		}
		return strings.Join([]string{
			"type=external_chain",
			"address=" + strings.TrimSpace(address),
			"network=" + cryptoNetwork(item.QuoteSnapshot),
		}, "|"), true
	}
	return "", false
}

func (r *orchestratorBatchMergeKeyResolver) resolveCardFundingAddress(snapshot *model.PaymentQuoteSnapshot) string {
	gatewayKey := destinationCardGatewayKeyFromQuote(snapshot)
	if gatewayKey == "" {
		return ""
	}
	route, ok := lookupCardGatewayRoute(r.cardGatewayRoutes, gatewayKey)
	if !ok {
		return ""
	}
	return strings.TrimSpace(route.FundingAddress)
}

func destinationCardGatewayKeyFromQuote(snapshot *model.PaymentQuoteSnapshot) string {
	if snapshot == nil || snapshot.Route == nil {
		return ""
	}
	hops := snapshot.Route.Hops
	fallback := ""
	for i := range hops {
		hop := hops[i]
		if hop == nil || model.ParseRail(hop.Rail) != discovery.RailCardPayout {
			continue
		}
		key := firstNonEmpty(strings.TrimSpace(hop.Gateway), strings.TrimSpace(hop.InstanceID))
		if key == "" {
			continue
		}
		if hop.Role != paymenttypes.QuoteRouteHopRoleDestination {
			fallback = key
			continue
		}
		return key
	}
	return fallback
}

func cryptoNetwork(snapshot *model.PaymentQuoteSnapshot) string {
	if snapshot == nil || snapshot.Route == nil {
		return ""
	}
	for i := range snapshot.Route.Hops {
		hop := snapshot.Route.Hops[i]
		if hop == nil || model.ParseRail(hop.Rail) != discovery.RailCrypto {
			continue
		}
		network := strings.TrimSpace(hop.Network)
		if network != "" {
			return strings.ToUpper(network)
		}
	}
	if strings.TrimSpace(snapshot.Route.Network) != "" {
		return strings.ToUpper(strings.TrimSpace(snapshot.Route.Network))
	}
	return ""
}
