package sendgridcli

import (
	"context"
	"encoding/json"
	"io"
	"net/http"
	"strings"
	"time"

	. "github.com/onsi/ginkgo/v2"
	. "github.com/onsi/gomega"
	"github.com/sendgrid/rest"
	"github.com/sendgrid/sendgrid-go"
	"github.com/sendgrid/sendgrid-go/helpers/mail"
)

type capturedSendGridRequest struct {
	message *mail.SGMailV3
	err     error
}

type roundTripFunc func(*http.Request) (*http.Response, error)

func (f roundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
	return f(req)
}

func newSendGridTestClient() (*client, <-chan capturedSendGridRequest, func()) {
	requests := make(chan capturedSendGridRequest, 1)
	originalClient := sendgrid.DefaultClient
	sendgrid.DefaultClient = &rest.Client{HTTPClient: &http.Client{Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) {
		message := &mail.SGMailV3{}
		err := json.NewDecoder(req.Body).Decode(message)
		requests <- capturedSendGridRequest{message: message, err: err}
		return &http.Response{
			StatusCode: http.StatusAccepted,
			Header:     make(http.Header),
			Body:       io.NopCloser(strings.NewReader("")),
		}, nil
	})}}

	raw := sendgrid.NewSendClient("test-api-key")
	return &client{raw: raw}, requests, func() {
		sendgrid.DefaultClient = originalClient
	}
}

var _ = Describe("SendBulkPersonalizedEmails", func() {
	It("creates an independent personalization for each non-empty recipient", func() {
		client, requests, restore := newSendGridTestClient()
		defer restore()

		deliveryTime := time.Date(2026, time.July, 17, 10, 30, 0, 0, time.UTC)
		err := client.SendBulkPersonalizedEmails(context.Background(), SendBulkPersonalizedEmailsRequest{
			From:       "sender@tidbcloud.com",
			To:         []string{" first@tidbcloud.com ", " ", "", "second@tidbcloud.com"},
			TemplateID: "template-id",
			TemplateVars: map[string]string{
				"subject": "Test subject",
				"body":    "Test body",
			},
			DeliveryTime: deliveryTime,
		})

		Expect(err).NotTo(HaveOccurred())
		request := <-requests
		Expect(request.err).NotTo(HaveOccurred())
		message := request.message
		Expect(message.From.Address).To(Equal("sender@tidbcloud.com"))
		Expect(message.TemplateID).To(Equal("template-id"))
		Expect(message.SendAt).To(Equal(int(deliveryTime.Unix())))
		Expect(message.Personalizations).To(HaveLen(2))
		Expect(message.Personalizations[0].To).To(HaveLen(1))
		Expect(message.Personalizations[0].To[0].Address).To(Equal("first@tidbcloud.com"))
		Expect(message.Personalizations[1].To).To(HaveLen(1))
		Expect(message.Personalizations[1].To[0].Address).To(Equal("second@tidbcloud.com"))

		for _, personalization := range message.Personalizations {
			Expect(personalization.DynamicTemplateData).To(Equal(map[string]interface{}{
				"subject": "Test subject",
				"body":    "Test body",
			}))
		}
	})

	It("accepts up to 1000 recipients", func() {
		client, requests, restore := newSendGridTestClient()
		defer restore()

		recipients := make([]string, MaxBulkPersonalizedEmailRecipients)
		for i := range recipients {
			recipients[i] = "recipient@tidbcloud.com"
		}

		err := client.SendBulkPersonalizedEmails(context.Background(), SendBulkPersonalizedEmailsRequest{
			To: recipients,
		})

		Expect(err).NotTo(HaveOccurred())
		request := <-requests
		Expect(request.err).NotTo(HaveOccurred())
		Expect(request.message.Personalizations).To(HaveLen(MaxBulkPersonalizedEmailRecipients))
	})

	It("rejects more than 1000 recipients before filtering empty addresses or calling SendGrid", func() {
		err := (&client{}).SendBulkPersonalizedEmails(context.Background(), SendBulkPersonalizedEmailsRequest{
			To: make([]string, MaxBulkPersonalizedEmailRecipients+1),
		})

		Expect(err).To(MatchError("too many recipients: got 1001, maximum is 1000"))
	})
})

var _ = Describe("SendPlainEmail", func() {
	It("uses provided from name when present", func() {
		from := plainEmailFrom(SendPlainEmailRequest{
			From:     "test@tidbcloud.com",
			FromName: "Custom Sender",
		})

		Expect(from.Name).To(Equal("Custom Sender"))
		Expect(from.Address).To(Equal("test@tidbcloud.com"))
	})

	It("falls back to the legacy from name when omitted", func() {
		from := plainEmailFrom(SendPlainEmailRequest{
			From: "test@tidbcloud.com",
		})

		Expect(from.Name).To(Equal(""))
		Expect(from.Address).To(Equal("test@tidbcloud.com"))
	})
})
