package quotation_service_v2

import (
	"strings"

	paymenttypes "github.com/tech/sendico/pkg/payments/types"
	paymentv1 "github.com/tech/sendico/pkg/proto/common/payment/v1"
	quotationv2 "github.com/tech/sendico/pkg/proto/payments/quotation/v2"
)

func modelRouteFromProto(src *quotationv2.RouteSpecification) *paymenttypes.QuoteRouteSpecification {
	if src == nil {
		return nil
	}
	result := &paymenttypes.QuoteRouteSpecification{
		Rail:              strings.TrimSpace(src.GetRail()),
		Provider:          strings.TrimSpace(src.GetProvider()),
		PayoutMethod:      strings.TrimSpace(src.GetPayoutMethod()),
		Settlement:        modelSettlementFromProto(src.GetSettlement()),
		Network:           strings.TrimSpace(src.GetNetwork()),
		RouteRef:          strings.TrimSpace(src.GetRouteRef()),
		PricingProfileRef: strings.TrimSpace(src.GetPricingProfileRef()),
	}
	if hops := src.GetHops(); len(hops) > 0 {
		result.Hops = make([]*paymenttypes.QuoteRouteHop, 0, len(hops))
		for _, hop := range hops {
			if hop == nil {
				continue
			}
			result.Hops = append(result.Hops, &paymenttypes.QuoteRouteHop{
				Index:      hop.GetIndex(),
				Rail:       strings.TrimSpace(hop.GetRail()),
				Gateway:    strings.TrimSpace(hop.GetGateway()),
				InstanceID: strings.TrimSpace(hop.GetInstanceId()),
				Network:    strings.TrimSpace(hop.GetNetwork()),
				Role:       routeHopRoleFromProto(hop.GetRole()),
			})
		}
		if len(result.Hops) == 0 {
			result.Hops = nil
		}
	}
	return result
}

func protoRouteFromModel(src *paymenttypes.QuoteRouteSpecification) *quotationv2.RouteSpecification {
	if src == nil {
		return nil
	}
	result := &quotationv2.RouteSpecification{
		Rail:              strings.TrimSpace(src.Rail),
		Provider:          strings.TrimSpace(src.Provider),
		PayoutMethod:      strings.TrimSpace(src.PayoutMethod),
		Network:           strings.TrimSpace(src.Network),
		RouteRef:          strings.TrimSpace(src.RouteRef),
		PricingProfileRef: strings.TrimSpace(src.PricingProfileRef),
		Settlement:        protoSettlementFromModel(src.Settlement),
	}
	if len(src.Hops) > 0 {
		result.Hops = make([]*quotationv2.RouteHop, 0, len(src.Hops))
		for _, hop := range src.Hops {
			if hop == nil {
				continue
			}
			result.Hops = append(result.Hops, &quotationv2.RouteHop{
				Index:      hop.Index,
				Rail:       strings.TrimSpace(hop.Rail),
				Gateway:    strings.TrimSpace(hop.Gateway),
				InstanceId: strings.TrimSpace(hop.InstanceID),
				Network:    strings.TrimSpace(hop.Network),
				Role:       routeHopRoleToProto(hop.Role),
			})
		}
		if len(result.Hops) == 0 {
			result.Hops = nil
		}
	}
	return result
}

func modelSettlementFromProto(src *quotationv2.RouteSettlement) *paymenttypes.QuoteRouteSettlement {
	if src == nil {
		return nil
	}

	result := &paymenttypes.QuoteRouteSettlement{
		Model: strings.TrimSpace(src.GetModel()),
	}
	if asset := src.GetAsset(); asset != nil {
		key := asset.GetKey()
		result.Asset = &paymenttypes.Asset{
			Chain:           strings.ToUpper(strings.TrimSpace(key.GetChain())),
			TokenSymbol:     strings.ToUpper(strings.TrimSpace(key.GetTokenSymbol())),
			ContractAddress: strings.TrimSpace(asset.GetContractAddress()),
		}
	}
	if result.Asset == nil && result.Model == "" {
		return nil
	}
	return result
}

func protoSettlementFromModel(src *paymenttypes.QuoteRouteSettlement) *quotationv2.RouteSettlement {
	if src == nil {
		return nil
	}
	result := &quotationv2.RouteSettlement{
		Model: strings.TrimSpace(src.Model),
	}
	if src.Asset != nil {
		result.Asset = &paymentv1.ChainAsset{
			Key: &paymentv1.ChainAssetKey{
				Chain:       strings.ToUpper(strings.TrimSpace(src.Asset.Chain)),
				TokenSymbol: strings.ToUpper(strings.TrimSpace(src.Asset.TokenSymbol)),
			},
		}
		if contract := strings.TrimSpace(src.Asset.ContractAddress); contract != "" {
			result.Asset.ContractAddress = &contract
		}
	}
	if result.Asset == nil && result.Model == "" {
		return nil
	}
	return result
}

func modelExecutionConditionsFromProto(src *quotationv2.ExecutionConditions) *paymenttypes.QuoteExecutionConditions {
	if src == nil {
		return nil
	}
	return &paymenttypes.QuoteExecutionConditions{
		Readiness:                         readinessFromProto(src.GetReadiness()),
		BatchingEligible:                  src.GetBatchingEligible(),
		PrefundingRequired:                src.GetPrefundingRequired(),
		PrefundingCostIncluded:            src.GetPrefundingCostIncluded(),
		LiquidityCheckRequiredAtExecution: src.GetLiquidityCheckRequiredAtExecution(),
		LatencyHint:                       strings.TrimSpace(src.GetLatencyHint()),
		Assumptions:                       cloneStringSlice(src.GetAssumptions()),
	}
}

func protoExecutionConditionsFromModel(src *paymenttypes.QuoteExecutionConditions) *quotationv2.ExecutionConditions {
	if src == nil {
		return nil
	}
	return &quotationv2.ExecutionConditions{
		Readiness:                         readinessToProto(src.Readiness),
		BatchingEligible:                  src.BatchingEligible,
		PrefundingRequired:                src.PrefundingRequired,
		PrefundingCostIncluded:            src.PrefundingCostIncluded,
		LiquidityCheckRequiredAtExecution: src.LiquidityCheckRequiredAtExecution,
		LatencyHint:                       strings.TrimSpace(src.LatencyHint),
		Assumptions:                       cloneStringSlice(src.Assumptions),
	}
}

func readinessFromProto(src quotationv2.QuoteExecutionReadiness) paymenttypes.QuoteExecutionReadiness {
	switch src {
	case quotationv2.QuoteExecutionReadiness_QUOTE_EXECUTION_READINESS_LIQUIDITY_READY:
		return paymenttypes.QuoteExecutionReadinessLiquidityReady
	case quotationv2.QuoteExecutionReadiness_QUOTE_EXECUTION_READINESS_LIQUIDITY_OBTAINABLE:
		return paymenttypes.QuoteExecutionReadinessLiquidityObtainable
	case quotationv2.QuoteExecutionReadiness_QUOTE_EXECUTION_READINESS_INDICATIVE:
		return paymenttypes.QuoteExecutionReadinessIndicative
	default:
		return paymenttypes.QuoteExecutionReadinessUnspecified
	}
}

func readinessToProto(src paymenttypes.QuoteExecutionReadiness) quotationv2.QuoteExecutionReadiness {
	switch src {
	case paymenttypes.QuoteExecutionReadinessLiquidityReady:
		return quotationv2.QuoteExecutionReadiness_QUOTE_EXECUTION_READINESS_LIQUIDITY_READY
	case paymenttypes.QuoteExecutionReadinessLiquidityObtainable:
		return quotationv2.QuoteExecutionReadiness_QUOTE_EXECUTION_READINESS_LIQUIDITY_OBTAINABLE
	case paymenttypes.QuoteExecutionReadinessIndicative:
		return quotationv2.QuoteExecutionReadiness_QUOTE_EXECUTION_READINESS_INDICATIVE
	default:
		return quotationv2.QuoteExecutionReadiness_QUOTE_EXECUTION_READINESS_UNSPECIFIED
	}
}

func cloneStringSlice(src []string) []string {
	if len(src) == 0 {
		return nil
	}
	result := make([]string, 0, len(src))
	for _, value := range src {
		trimmed := strings.TrimSpace(value)
		if trimmed == "" {
			continue
		}
		result = append(result, trimmed)
	}
	if len(result) == 0 {
		return nil
	}
	return result
}

func routeHopRoleFromProto(src quotationv2.RouteHopRole) paymenttypes.QuoteRouteHopRole {
	switch src {
	case quotationv2.RouteHopRole_ROUTE_HOP_ROLE_SOURCE:
		return paymenttypes.QuoteRouteHopRoleSource
	case quotationv2.RouteHopRole_ROUTE_HOP_ROLE_TRANSIT:
		return paymenttypes.QuoteRouteHopRoleTransit
	case quotationv2.RouteHopRole_ROUTE_HOP_ROLE_DESTINATION:
		return paymenttypes.QuoteRouteHopRoleDestination
	default:
		return paymenttypes.QuoteRouteHopRoleUnspecified
	}
}

func routeHopRoleToProto(src paymenttypes.QuoteRouteHopRole) quotationv2.RouteHopRole {
	switch src {
	case paymenttypes.QuoteRouteHopRoleSource:
		return quotationv2.RouteHopRole_ROUTE_HOP_ROLE_SOURCE
	case paymenttypes.QuoteRouteHopRoleTransit:
		return quotationv2.RouteHopRole_ROUTE_HOP_ROLE_TRANSIT
	case paymenttypes.QuoteRouteHopRoleDestination:
		return quotationv2.RouteHopRole_ROUTE_HOP_ROLE_DESTINATION
	default:
		return quotationv2.RouteHopRole_ROUTE_HOP_ROLE_UNSPECIFIED
	}
}
