package srequest

import (
	"strings"

	"github.com/tech/sendico/pkg/merrors"
)

type PaymentBase struct {
	IdempotencyKey string            `json:"idempotencyKey"`
	Metadata       map[string]string `json:"metadata,omitempty"`
}

func (b *PaymentBase) Validate() error {
	if b.IdempotencyKey == "" {
		return merrors.InvalidArgument("idempotencyKey is required", "idempotencyKey")
	}
	return nil
}

type QuotePayment struct {
	PaymentBase `json:",inline"`
	Intent      PaymentIntent `json:"intent"`
	PreviewOnly bool          `json:"previewOnly"`
}

func (r *QuotePayment) Validate() error {
	if err := validateQuoteIdempotency(r.PreviewOnly, r.IdempotencyKey); err != nil {
		return err
	}

	// intent is mandatory, so validate always
	if err := r.Intent.Validate(); err != nil {
		return err
	}

	return nil
}

type QuotePayments struct {
	PaymentBase `json:",inline"`
	Intents     []PaymentIntent `json:"intents"`
	PreviewOnly bool            `json:"previewOnly"`
}

func (r *QuotePayments) Validate() error {
	if err := validateQuoteIdempotency(r.PreviewOnly, r.IdempotencyKey); err != nil {
		return err
	}
	if len(r.Intents) == 0 {
		return merrors.InvalidArgument("intents are required", "intents")
	}
	for i := range r.Intents {
		if err := r.Intents[i].Validate(); err != nil {
			return err
		}
	}
	return nil
}

func validateQuoteIdempotency(previewOnly bool, idempotencyKey string) error {
	key := strings.TrimSpace(idempotencyKey)
	if previewOnly {
		if key != "" {
			return merrors.InvalidArgument("previewOnly requests must not include idempotencyKey", "idempotencyKey")
		}
		return nil
	}
	if key == "" {
		return merrors.InvalidArgument("idempotencyKey is required", "idempotencyKey")
	}
	return nil
}

type InitiatePayment struct {
	PaymentBase      `json:",inline"`
	Intent           *PaymentIntent `json:"intent,omitempty"`
	QuoteRef         string         `json:"quoteRef,omitempty"`
	ClientPaymentRef string         `json:"clientPaymentRef,omitempty"`
}

func (r InitiatePayment) Validate() error {
	// base checks
	if err := r.PaymentBase.Validate(); err != nil {
		return err
	}

	hasIntent := r.Intent != nil
	hasQuote := r.QuoteRef != ""

	// must be exactly one
	switch {
	case !hasIntent && !hasQuote:
		return merrors.NoData("either intent or quoteRef must be provided")
	case hasIntent && hasQuote:
		return merrors.DataConflict("intent and quoteRef are mutually exclusive")
	}

	// if intent provided → validate it
	if hasIntent {
		if err := r.Intent.Validate(); err != nil {
			return err
		}
	}

	return nil
}

type InitiatePayments struct {
	PaymentBase      `json:",inline"`
	QuoteRef         string `json:"quoteRef,omitempty"`
	ClientPaymentRef string `json:"clientPaymentRef,omitempty"`
}

func (r *InitiatePayments) Validate() error {
	if r == nil {
		return merrors.InvalidArgument("request is required")
	}
	if err := r.PaymentBase.Validate(); err != nil {
		return err
	}
	r.QuoteRef = strings.TrimSpace(r.QuoteRef)

	if r.QuoteRef == "" {
		return merrors.InvalidArgument("quoteRef is required", "quoteRef")
	}
	return nil
}
