package email_test

import (
	"context"
	"fmt"
	"testing"

	"gateway/internal/model/notification/provider/email"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

const testRecipientEmail = "x@test.com"

func TestChain_Send_FailoverToSecondProvider(t *testing.T) {
	primary := email.NewMockSender(
		email.WithMockName("primary"),
		email.WithMockSort(1),
		email.WithMockError(fmt.Errorf("primary down")),
	)
	backup := email.NewMockSender(
		email.WithMockName("backup"),
		email.WithMockSort(2),
		email.WithMockMessageID("backup-123"),
	)

	chain := email.NewChain(primary, backup)
	provider, id, err := chain.Send(context.Background(), &email.Message{
		From:    "noreply@test.com",
		To:      []string{"user@test.com"},
		Subject: "verify",
		Body:    "<p>code</p>",
	})

	require.NoError(t, err)
	assert.Equal(t, "backup", provider)
	assert.Equal(t, "backup-123", id)
	assert.Len(t, primary.Calls(), 1)
	assert.Len(t, backup.Calls(), 1)
}

func TestChain_Send_TriesLowerSortFirst(t *testing.T) {
	var order []string
	mk := func(name string, sort int, fail bool) *email.MockSender {
		m := email.NewMockSender(email.WithMockName(name), email.WithMockSort(sort))
		m.SendHook = func(context.Context, *email.Message) (string, error) {
			order = append(order, name)
			if fail {
				return "", fmt.Errorf("%s failed", name)
			}
			return name + "-id", nil
		}
		return m
	}

	// Unsorted registration; chain must try a(1) → b(2) succeed, never c(3).
	chain := email.NewChain(mk("c", 3, true), mk("a", 1, true), mk("b", 2, false))
	provider, _, err := chain.Send(context.Background(), &email.Message{To: []string{testRecipientEmail}})
	require.NoError(t, err)
	assert.Equal(t, "b", provider)
	assert.Equal(t, []string{"a", "b"}, order)
}

func TestChain_Send_AllProvidersFail(t *testing.T) {
	chain := email.NewChain(
		email.ErrSender("a", 1, fmt.Errorf("a fail")),
		email.ErrSender("b", 2, fmt.Errorf("b fail")),
	)
	_, _, err := chain.Send(context.Background(), &email.Message{To: []string{testRecipientEmail}})
	require.Error(t, err)
	assert.Contains(t, err.Error(), "all providers failed")
}

func TestChain_Send_NoSenders(t *testing.T) {
	chain := email.NewChain()
	_, _, err := chain.Send(context.Background(), &email.Message{To: []string{testRecipientEmail}})
	require.Error(t, err)
	assert.Contains(t, err.Error(), "no senders")
}
