package quotation_service_v2

import (
	"fmt"
	"strings"

	"github.com/tech/sendico/pkg/mlogger"
	"github.com/tech/sendico/pkg/mutil/mzap"
	moneyv1 "github.com/tech/sendico/pkg/proto/common/money/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"
	"go.uber.org/zap"
)

type endpointLogSummary struct {
	ResolutionType string `json:"resolutionType"`
	MethodRef      string `json:"methodRef,omitempty"`
	MethodType     string `json:"methodType,omitempty"`
	RecipientRef   string `json:"recipientRef,omitempty"`
	PayeeRef       string `json:"payeeRef,omitempty"`
	DataBytes      int    `json:"dataBytes,omitempty"`
}

type quoteIntentLogSummary struct {
	Source             endpointLogSummary `json:"source"`
	Destination        endpointLogSummary `json:"destination"`
	Amount             string             `json:"amount,omitempty"`
	SettlementMode     string             `json:"settlementMode,omitempty"`
	FeeTreatment       string             `json:"feeTreatment,omitempty"`
	SettlementCurrency string             `json:"settlementCurrency,omitempty"`
	HasFX              bool               `json:"hasFx"`
	FXPair             string             `json:"fxPair,omitempty"`
	FXSide             string             `json:"fxSide,omitempty"`
	HasComment         bool               `json:"hasComment"`
}

func (s *QuotationServiceV2) quotePaymentLogger(req *quotationv2.QuotePaymentRequest) mlogger.Logger {
	return s.logger.With(
		mzap.ObjRef("flow_ref", bson.NewObjectID()),
		zap.String("rpc_method", "QuotePayment"),
		zap.String("organization_ref", strings.TrimSpace(req.GetMeta().GetOrganizationRef())),
		zap.String("idempotency_key", strings.TrimSpace(req.GetIdempotencyKey())),
		zap.Bool("preview_only", req.GetPreviewOnly()),
		zap.String("initiator_ref", strings.TrimSpace(req.GetInitiatorRef())),
	)
}

func (s *QuotationServiceV2) quotePaymentsLogger(req *quotationv2.QuotePaymentsRequest) mlogger.Logger {
	return s.logger.With(
		mzap.ObjRef("flow_ref", bson.NewObjectID()),
		zap.String("rpc_method", "QuotePayments"),
		zap.String("organization_ref", strings.TrimSpace(req.GetMeta().GetOrganizationRef())),
		zap.String("idempotency_key", strings.TrimSpace(req.GetIdempotencyKey())),
		zap.Bool("preview_only", req.GetPreviewOnly()),
		zap.String("initiator_ref", strings.TrimSpace(req.GetInitiatorRef())),
		zap.Int("intent_count", len(req.GetIntents())),
	)
}

func summarizeQuoteIntent(intent *quotationv2.QuoteIntent) *quoteIntentLogSummary {
	if intent == nil {
		return nil
	}
	fxPair, fxSide, hasFX := summarizeFXIntent(intent.GetFx())
	return &quoteIntentLogSummary{
		Source:             summarizeEndpoint(intent.GetSource()),
		Destination:        summarizeEndpoint(intent.GetDestination()),
		Amount:             moneyLogValue(intent.GetAmount()),
		SettlementMode:     enumLogValue(intent.GetSettlementMode().String()),
		FeeTreatment:       enumLogValue(intent.GetFeeTreatment().String()),
		SettlementCurrency: strings.ToUpper(strings.TrimSpace(intent.GetSettlementCurrency())),
		HasFX:              hasFX,
		FXPair:             fxPair,
		FXSide:             fxSide,
		HasComment:         strings.TrimSpace(intent.GetComment()) != "",
	}
}

func summarizeQuoteIntentList(intents []*quotationv2.QuoteIntent, limit int) ([]quoteIntentLogSummary, int, int) {
	total := len(intents)
	if total == 0 || limit <= 0 {
		return nil, total, 0
	}
	if limit > total {
		limit = total
	}

	items := make([]quoteIntentLogSummary, 0, limit)
	for i := 0; i < limit; i++ {
		if summary := summarizeQuoteIntent(intents[i]); summary != nil {
			items = append(items, *summary)
			continue
		}
		items = append(items, quoteIntentLogSummary{})
	}
	return items, total, total - limit
}

func summarizeEndpoint(endpoint *endpointv1.PaymentEndpoint) endpointLogSummary {
	if endpoint == nil {
		return endpointLogSummary{ResolutionType: "unspecified"}
	}
	switch source := endpoint.GetSource().(type) {
	case *endpointv1.PaymentEndpoint_PaymentMethodRef:
		return endpointLogSummary{
			ResolutionType: "payment_method_ref",
			MethodRef:      strings.TrimSpace(source.PaymentMethodRef),
		}
	case *endpointv1.PaymentEndpoint_PaymentMethod:
		method := source.PaymentMethod
		if method == nil {
			return endpointLogSummary{ResolutionType: "payment_method"}
		}
		return endpointLogSummary{
			ResolutionType: "payment_method",
			MethodType:     enumLogValue(method.GetType().String()),
			RecipientRef:   strings.TrimSpace(method.GetRecipientRef()),
			DataBytes:      len(method.GetData()),
		}
	case *endpointv1.PaymentEndpoint_PayeeRef:
		return endpointLogSummary{
			ResolutionType: "payee_ref",
			PayeeRef:       strings.TrimSpace(source.PayeeRef),
		}
	default:
		return endpointLogSummary{ResolutionType: "unspecified"}
	}
}

func moneyLogValue(m *moneyv1.Money) string {
	if m == nil {
		return ""
	}
	amount := strings.TrimSpace(m.GetAmount())
	currency := strings.ToUpper(strings.TrimSpace(m.GetCurrency()))
	switch {
	case amount == "" && currency == "":
		return ""
	case amount == "":
		return currency
	case currency == "":
		return amount
	default:
		return fmt.Sprintf("%s %s", amount, currency)
	}
}

func enumLogValue(value string) string {
	return strings.ToLower(strings.TrimSpace(value))
}

func summarizeFXIntent(fx *sharedv1.FXIntent) (string, string, bool) {
	if fx == nil || fx.GetPair() == nil {
		return "", "", false
	}
	base := strings.ToUpper(strings.TrimSpace(fx.GetPair().GetBase()))
	quote := strings.ToUpper(strings.TrimSpace(fx.GetPair().GetQuote()))
	if base == "" && quote == "" {
		return "", enumLogValue(fx.GetSide().String()), false
	}
	return strings.TrimSpace(base + "/" + quote), enumLogValue(fx.GetSide().String()), true
}
