package srequest

import (
	"regexp"
	"strings"

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

// AssetResolver defines environment-specific supported assets.
// Implementations should check:
// - fiat assets (ISO-4217)
// - crypto assets supported by gateways / FX providers
type AssetResolver interface {
	IsSupported(ticker string) bool
}

// Precompile regex for efficiency.
var currencySyntax = regexp.MustCompile(`^[A-Z0-9]{2,10}$`)

// ValidateCurrency validates currency syntax and checks dictionary via assetResolver.
func ValidateCurrency(cur string, assetResolver AssetResolver) error {
	// Basic presence
	if strings.TrimSpace(cur) == "" {
		return merrors.InvalidArgument("currency is required", "intent.currency")
	}

	// Normalize
	cur = strings.ToUpper(strings.TrimSpace(cur))

	// Syntax check
	if !currencySyntax.MatchString(cur) {
		return merrors.InvalidArgument(
			"invalid currency format (must be A–Z0–9, length 2–10)",
			"intent.currency",
		)
	}

	// Dictionary validation
	if assetResolver == nil {
		return merrors.InvalidArgument("asset resolver is not configured", "intent.currency")
	}

	if !assetResolver.IsSupported(cur) {
		return merrors.InvalidArgument("unsupported currency/asset", "intent.currency")
	}

	return nil
}

func ValidateMoney(m *paymenttypes.Money, assetResolver AssetResolver) error {
	if m == nil {
		return merrors.InvalidArgument("money is required", "intent.amount")
	}

	// 1) Basic presence
	if strings.TrimSpace(m.Amount) == "" {
		return merrors.InvalidArgument("amount is required", "intent.amount")
	}

	// 2) Validate decimal amount
	amount, err := decimal.NewFromString(m.Amount)
	if err != nil {
		return merrors.InvalidArgument("invalid decimal amount", "intent.amount")
	}
	if amount.IsNegative() {
		return merrors.InvalidArgument("amount must be >= 0", "intent.amount")
	}

	// 3) Validate currency via helper
	if err := ValidateCurrency(m.Currency, assetResolver); err != nil {
		return err
	}

	return nil
}

type CurrencyPair struct {
	Base  string `json:"base"`
	Quote string `json:"quote"`
}

func (p *CurrencyPair) Validate() error {
	if p == nil {
		return merrors.InvalidArgument("currency pair is required", "currncy_pair")
	}
	if err := ValidateCurrency(p.Base, &AssetResolverStub{}); err != nil {
		return merrors.InvalidArgument("invalid base currency in pair: "+err.Error(), "currency_pair.base")
	}
	if err := ValidateCurrency(p.Quote, &AssetResolverStub{}); err != nil {
		return merrors.InvalidArgument("invalid quote currency in pair: "+err.Error(), "currency_pair.quote")
	}
	return nil
}

type FXIntent struct {
	Pair              *CurrencyPair `json:"pair,omitempty"`
	Side              FXSide        `json:"side,omitempty"`
	Firm              bool          `json:"firm,omitempty"`
	TTLms             int64         `json:"ttl_ms,omitempty"`
	PreferredProvider string        `json:"preferred_provider,omitempty"`
	MaxAgeMs          int32         `json:"max_age_ms,omitempty"`
}

func (fx *FXIntent) Validate() error {
	if fx.Pair == nil {
		return merrors.InvalidArgument("fx pair is required", "intent.fx.pair")
	}
	if err := fx.Pair.Validate(); err != nil {
		return err
	}

	switch strings.TrimSpace(string(fx.Side)) {
	case string(FXSideBuyBaseSellQuote), string(FXSideSellBaseBuyQuote):
	default:
		return merrors.InvalidArgument("fx side is invalid", "intent.fx.side")
	}

	if fx.TTLms < 0 {
		return merrors.InvalidArgument("fx ttl_ms cannot be negative", "intent.fx.ttl_ms")
	}
	if fx.TTLms == 0 && fx.Firm {
		return merrors.InvalidArgument("firm quote requires positive ttl_ms", "intent.fx.ttl_ms")
	}

	if fx.MaxAgeMs < 0 {
		return merrors.InvalidArgument("fx max_age_ms cannot be negative", "intent.fx.max_age_ms")
	}

	return nil
}
