package email

import (
	"sync"
	"testing"
	"time"

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

func TestSend(t *testing.T) {
	config := &Config{
		SMTPHost:  "smtp.example.com",
		SMTPPort:  587,
		FromEmail: "test@example.com",
	}

	message := &Message{
		To:      []string{"recipient@example.com"},
		Subject: "Test",
		Body:    "Test body",
	}

	// This will fail to connect (no real server), but should validate inputs
	err := Send(config, message)
	// We expect an error because there's no real SMTP server
	// But it should be a connection error, not a validation error
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "dial")
}

func TestSendValidation(t *testing.T) {
	tests := []struct {
		name    string
		config  *Config
		message *Message
		wantErr bool
		errMsg  string
	}{
		{
			name:    "nil config",
			config:  nil,
			message: &Message{To: []string{"test@example.com"}, Subject: "Test", Body: "Test"},
			wantErr: true,
			errMsg:  "configuration is required",
		},
		{
			name:    "nil message",
			config:  &Config{SMTPHost: "smtp.example.com", SMTPPort: 587, FromEmail: "from@example.com"},
			message: nil,
			wantErr: true,
			errMsg:  "message is required",
		},
		{
			name: "invalid config",
			config: &Config{
				SMTPHost: "",
				SMTPPort: 587,
			},
			message: &Message{To: []string{"test@example.com"}, Subject: "Test", Body: "Test"},
			wantErr: true,
			errMsg:  "invalid email configuration",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := Send(tt.config, tt.message)
			if tt.wantErr {
				assert.Error(t, err)
				assert.Contains(t, err.Error(), tt.errMsg)
			}
		})
	}
}

func TestSendAsync(t *testing.T) {
	config := &Config{
		SMTPHost:  "smtp.example.com",
		SMTPPort:  587,
		FromEmail: "test@example.com",
	}

	message := &Message{
		To:      []string{"recipient@example.com"},
		Subject: "Test Async",
		Body:    "Test async body",
	}

	// SendAsync should not block
	start := time.Now()
	SendAsync(config, message)
	duration := time.Since(start)

	// Should return almost immediately (< 100ms)
	assert.Less(t, duration, 100*time.Millisecond)

	// Give goroutine time to start
	time.Sleep(50 * time.Millisecond)
}

func TestSendAsyncConcurrent(t *testing.T) {
	config := &Config{
		SMTPHost:  "smtp.example.com",
		SMTPPort:  587,
		FromEmail: "test@example.com",
	}

	// Send multiple emails concurrently
	var wg sync.WaitGroup
	count := 5

	for i := 0; i < count; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			message := &Message{
				To:      []string{"recipient@example.com"},
				Subject: "Concurrent Test",
				Body:    "Test body",
			}
			SendAsync(config, message)
		}()
	}

	// Should complete without deadlock
	done := make(chan bool)
	go func() {
		wg.Wait()
		done <- true
	}()

	select {
	case <-done:
		// Success
	case <-time.After(1 * time.Second):
		t.Fatal("SendAsync calls did not complete in time")
	}
}
