package opagg

import (
	"strings"

	"github.com/shopspring/decimal"
	"github.com/tech/sendico/payments/storage/model"
	"github.com/tech/sendico/pkg/merrors"
	paymenttypes "github.com/tech/sendico/pkg/payments/types"
)

func mergeIntentSnapshot(dst *model.PaymentIntent, src model.PaymentIntent) error {
	if dst == nil {
		return merrors.InvalidArgument("intent_snapshot is required")
	}

	sum, err := mergeMoney(dst.Amount, src.Amount, "intent_snapshot.amount")
	if err != nil {
		return err
	}
	dst.Amount = sum

	if dst.SettlementCurrency == "" {
		dst.SettlementCurrency = strings.TrimSpace(src.SettlementCurrency)
	} else if srcCurrency := strings.TrimSpace(src.SettlementCurrency); srcCurrency != "" && !strings.EqualFold(dst.SettlementCurrency, srcCurrency) {
		return merrors.InvalidArgument("intent_snapshot.settlement_currency mismatch")
	}

	if dst.Attributes == nil {
		dst.Attributes = map[string]string{}
	}
	for key, value := range src.Attributes {
		k := strings.TrimSpace(key)
		if k == "" {
			continue
		}
		if _, exists := dst.Attributes[k]; exists {
			continue
		}
		dst.Attributes[k] = strings.TrimSpace(value)
	}
	return nil
}

func mergeQuoteSnapshot(dst *model.PaymentQuoteSnapshot, src *model.PaymentQuoteSnapshot) error {
	if dst == nil {
		return merrors.InvalidArgument("quote_snapshot is required")
	}
	if src == nil {
		return nil
	}

	var err error
	dst.DebitAmount, err = mergeMoney(dst.DebitAmount, src.DebitAmount, "quote_snapshot.debit_amount")
	if err != nil {
		return err
	}
	dst.DebitSettlementAmount, err = mergeMoney(dst.DebitSettlementAmount, src.DebitSettlementAmount, "quote_snapshot.debit_settlement_amount")
	if err != nil {
		return err
	}
	dst.ExpectedSettlementAmount, err = mergeMoney(dst.ExpectedSettlementAmount, src.ExpectedSettlementAmount, "quote_snapshot.expected_settlement_amount")
	if err != nil {
		return err
	}
	dst.ExpectedFeeTotal, err = mergeMoney(dst.ExpectedFeeTotal, src.ExpectedFeeTotal, "quote_snapshot.expected_fee_total")
	if err != nil {
		return err
	}
	dst.TotalCost, err = mergeMoney(dst.TotalCost, src.TotalCost, "quote_snapshot.total_cost")
	if err != nil {
		return err
	}

	dst.NetworkFee, err = mergeNetworkFee(dst.NetworkFee, src.NetworkFee)
	if err != nil {
		return err
	}
	dst.FeeLines, err = mergeFeeLines(dst.FeeLines, src.FeeLines)
	if err != nil {
		return err
	}
	dst.FeeRules = mergeFeeRules(dst.FeeRules, src.FeeRules)

	dst.Route, err = mergeRoute(dst.Route, src.Route)
	if err != nil {
		return err
	}
	dst.ExecutionConditions = mergeExecutionConditions(dst.ExecutionConditions, src.ExecutionConditions)

	dst.FXQuote, err = mergeFXQuote(dst.FXQuote, src.FXQuote)
	if err != nil {
		return err
	}

	if strings.TrimSpace(dst.QuoteRef) == "" {
		dst.QuoteRef = strings.TrimSpace(src.QuoteRef)
	}

	return nil
}

func mergeMoney(dst, src *paymenttypes.Money, field string) (*paymenttypes.Money, error) {
	if dst == nil {
		return cloneMoney(src), nil
	}
	if src == nil {
		return dst, nil
	}

	dstCurrency := normalizeCurrency(dst.Currency)
	srcCurrency := normalizeCurrency(src.Currency)
	if dstCurrency == "" || srcCurrency == "" {
		return nil, merrors.InvalidArgument(field + ": currency is required")
	}
	if dstCurrency != srcCurrency {
		return nil, merrors.InvalidArgument(field + ": currency mismatch")
	}

	left, err := parseDecimal(dst.Amount)
	if err != nil {
		return nil, merrors.InvalidArgument(field + ": invalid amount")
	}
	right, err := parseDecimal(src.Amount)
	if err != nil {
		return nil, merrors.InvalidArgument(field + ": invalid amount")
	}
	dst.Amount = left.Add(right).String()
	dst.Currency = dstCurrency
	return dst, nil
}

func parseDecimal(raw string) (decimal.Decimal, error) {
	return decimal.NewFromString(strings.TrimSpace(raw))
}
