package xplan

import (
	"fmt"
	"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 planFeeHops(
	hops []normalizedHop,
	intent model.PaymentIntent,
	quote *model.PaymentQuoteSnapshot,
) (map[string]struct{}, error) {
	_, hasFee, err := feeAmountFromSnapshots(intent, quote)
	if err != nil {
		return nil, err
	}
	if !hasFee {
		return map[string]struct{}{}, nil
	}

	sourceHop, ok := sourceFeeHop(hops)
	if !ok {
		return map[string]struct{}{}, nil
	}
	return map[string]struct{}{
		observedKey(sourceHop): {},
	}, nil
}

func sourceFeeHop(hops []normalizedHop) (normalizedHop, bool) {
	for i := range hops {
		if hops[i].role == paymenttypes.QuoteRouteHopRoleSource {
			return hops[i], true
		}
	}
	if len(hops) > 0 {
		return hops[0], true
	}
	return normalizedHop{}, false
}

func feeAmountFromSnapshots(
	intent model.PaymentIntent,
	quote *model.PaymentQuoteSnapshot,
) (*paymenttypes.Money, bool, error) {
	if quote == nil || len(quote.FeeLines) == 0 {
		return nil, false, nil
	}

	sourceCurrency := ""
	if source := feePlanningSourceAmount(intent, quote); source != nil {
		sourceCurrency = strings.TrimSpace(source.Currency)
	}

	total := decimal.Zero
	currency := ""
	for i, line := range quote.FeeLines {
		money := feeLineMoney(line)
		if money == nil {
			continue
		}

		lineCurrency := strings.TrimSpace(money.GetCurrency())
		if lineCurrency == "" {
			return nil, false, merrors.InvalidArgument(fmt.Sprintf("quote_snapshot.fee_lines[%d].money.currency is required", i))
		}
		if sourceCurrency != "" && !strings.EqualFold(sourceCurrency, lineCurrency) {
			continue
		}
		if currency == "" {
			currency = lineCurrency
		} else if !strings.EqualFold(currency, lineCurrency) {
			return nil, false, merrors.InvalidArgument("quote_snapshot.fee_lines fee currency mismatch")
		}

		amountRaw := strings.TrimSpace(money.GetAmount())
		amount, err := decimal.NewFromString(amountRaw)
		if err != nil {
			return nil, false, merrors.InvalidArgument(fmt.Sprintf("quote_snapshot.fee_lines[%d].money.amount is invalid", i))
		}
		amount = amount.Abs()
		if amount.IsZero() {
			continue
		}
		if line.GetSide() == paymenttypes.EntrySideCredit {
			total = total.Sub(amount)
			continue
		}
		total = total.Add(amount)
	}

	if total.Sign() <= 0 {
		return nil, false, nil
	}
	return &paymenttypes.Money{
		Amount:   total.String(),
		Currency: strings.ToUpper(strings.TrimSpace(currency)),
	}, true, nil
}

func feePlanningSourceAmount(intent model.PaymentIntent, quote *model.PaymentQuoteSnapshot) *paymenttypes.Money {
	if quote != nil && quote.DebitAmount != nil {
		return quote.DebitAmount
	}
	return intent.Amount
}

func feeLineMoney(line *paymenttypes.FeeLine) *paymenttypes.Money {
	if line == nil {
		return nil
	}
	return line.GetMoney()
}
