package email

import (
	"context"
	"fmt"
	"sort"
	"time"
)

const defaultSendTimeout = 10 * time.Second

// Chain tries senders in ascending Sort order until one succeeds.
type Chain struct {
	senders []Sender
	timeout time.Duration
}

type ChainOption func(*Chain)

func WithTimeout(d time.Duration) ChainOption {
	return func(c *Chain) {
		c.timeout = d
	}
}

func NewChain(senders ...Sender) *Chain {
	c := &Chain{
		senders: append([]Sender(nil), senders...),
		timeout: defaultSendTimeout,
	}
	c.sortSenders()
	return c
}

func NewChainWithOptions(senders []Sender, opts ...ChainOption) *Chain {
	c := &Chain{
		senders: append([]Sender(nil), senders...),
		timeout: defaultSendTimeout,
	}
	for _, opt := range opts {
		opt(c)
	}
	c.sortSenders()
	return c
}

func (c *Chain) sortSenders() {
	sort.Slice(c.senders, func(i, j int) bool {
		return c.senders[i].Sort() < c.senders[j].Sort()
	})
}

// Send returns the winning provider name and provider-assigned message id.
func (c *Chain) Send(ctx context.Context, msg *Message) (providerName, messageID string, err error) {
	if len(c.senders) == 0 {
		return "", "", fmt.Errorf("email: no senders configured")
	}
	if msg == nil {
		return "", "", fmt.Errorf("email: message is nil")
	}

	sendCtx, cancel := context.WithTimeout(ctx, c.timeout)
	defer cancel()

	var lastErr error
	for _, sender := range c.senders {
		id, sendErr := sender.Send(sendCtx, msg)
		if sendErr == nil {
			return sender.Name(), id, nil
		}
		lastErr = sendErr
	}

	if lastErr == nil {
		lastErr = fmt.Errorf("email: all providers failed")
	}
	return "", "", fmt.Errorf("email: all providers failed: %w", lastErr)
}
