package email

import (
	"context"
	"encoding/json"
	"errors"
	"io"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
)

func TestPostmarkSender_Send(t *testing.T) {
	var gotBody postmarkRequest
	var gotToken string
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		gotToken = r.Header.Get("X-Postmark-Server-Token")
		body, _ := io.ReadAll(r.Body)
		_ = json.Unmarshal(body, &gotBody)
		w.Header().Set("Content-Type", "application/json")
		_, _ = w.Write([]byte(`{"ErrorCode":0,"Message":"OK","To":"alice@example.com"}`))
	}))
	defer srv.Close()

	sender := NewPostmarkSender("test-token", "noreply@example.com", srv.URL, testLogger(t))
	if err := sender.Send(context.Background(), []string{"alice@example.com"}, "Hello", "World"); err != nil {
		t.Fatalf("Send: %v", err)
	}
	if gotToken != "test-token" {
		t.Errorf("token = %q, want test-token", gotToken)
	}
	if gotBody.From != "noreply@example.com" {
		t.Errorf("from = %q", gotBody.From)
	}
	if gotBody.To != "alice@example.com" {
		t.Errorf("to = %q", gotBody.To)
	}
	if gotBody.Subject != "Hello" {
		t.Errorf("subject = %q", gotBody.Subject)
	}
	if gotBody.TextBody != "World" {
		t.Errorf("body = %q", gotBody.TextBody)
	}
}

func TestPostmarkSender_ErrorCode(t *testing.T) {
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		_, _ = w.Write([]byte(`{"ErrorCode":406,"Message":"Inactive recipient"}`))
	}))
	defer srv.Close()

	sender := NewPostmarkSender("tok", "noreply@example.com", srv.URL, testLogger(t))
	err := sender.Send(context.Background(), []string{"x@x.com"}, "s", "b")
	if err == nil {
		t.Fatal("expected error, got nil")
	}
	if !strings.Contains(err.Error(), "406") {
		t.Errorf("error should mention code 406: %v", err)
	}
}

func TestPostmarkSender_ServerError_Temporary(t *testing.T) {
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusInternalServerError)
		_, _ = w.Write([]byte("boom"))
	}))
	defer srv.Close()

	sender := NewPostmarkSender("tok", "noreply@example.com", srv.URL, testLogger(t))
	err := sender.Send(context.Background(), []string{"x@x.com"}, "s", "b")
	if err == nil {
		t.Fatal("expected error")
	}
	var temp *TemporaryError
	if !errors.As(err, &temp) {
		t.Errorf("expected TemporaryError, got %T: %v", err, err)
	}
}

func TestPostmarkSender_Validation(t *testing.T) {
	sender := NewPostmarkSender("", "noreply@example.com", "", testLogger(t))
	if err := sender.Send(context.Background(), []string{"x@x.com"}, "s", "b"); err == nil {
		t.Fatal("expected error for missing token")
	}
	sender = NewPostmarkSender("tok", "", "", testLogger(t))
	if err := sender.Send(context.Background(), []string{"x@x.com"}, "s", "b"); err == nil {
		t.Fatal("expected error for missing from")
	}
	sender = NewPostmarkSender("tok", "noreply@example.com", "", testLogger(t))
	if err := sender.Send(context.Background(), nil, "s", "b"); err == nil {
		t.Fatal("expected error for empty recipients")
	}
}

func TestPostmarkSender_MultipleRecipients(t *testing.T) {
	var gotBody postmarkRequest
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		body, _ := io.ReadAll(r.Body)
		_ = json.Unmarshal(body, &gotBody)
		_, _ = w.Write([]byte(`{"ErrorCode":0,"Message":"OK"}`))
	}))
	defer srv.Close()

	sender := NewPostmarkSender("tok", "noreply@example.com", srv.URL, testLogger(t))
	if err := sender.Send(context.Background(), []string{"a@x.com", "b@x.com"}, "s", "b"); err != nil {
		t.Fatalf("Send: %v", err)
	}
	if !strings.Contains(gotBody.To, "a@x.com") || !strings.Contains(gotBody.To, "b@x.com") {
		t.Errorf("to = %q, expected both recipients", gotBody.To)
	}
}
