package services

import (
	"strings"
	"testing"
)

// TestHTMLToText covers the HTML→plain-text fallback. Users with text-only
// clients still need to read reminder/invite mails, and some spam filters
// downrank multipart/alternative when the text part is empty or identical
// to the HTML.
func TestHTMLToText(t *testing.T) {
	in := `<html><head><style>b{color:red}</style></head><body>` +
		`<h1>Deadline &uuml;berf&auml;llig</h1><p>Hallo <b>Welt</b></p>` +
		`<p>Zweite Zeile &mdash; ok.</p><script>alert(1)</script></body></html>`
	got := htmlToText(in)
	if !strings.Contains(got, "Deadline überfällig") {
		t.Errorf("expected decoded umlauts in %q", got)
	}
	if strings.Contains(got, "alert(1)") {
		t.Errorf("script content leaked into text body: %q", got)
	}
	if strings.Contains(got, "<b>") {
		t.Errorf("raw tag remained in text body: %q", got)
	}
	if !strings.Contains(got, "—") {
		t.Errorf("expected em-dash decoded, got %q", got)
	}
}

// TestRenderTemplateDeadlineDigest verifies the bundled-digest template
// renders all three category sections, applies the DRINGEND wording on the
// evening slot, and folds in IsOtherOwner labels when a row's owner isn't
// the recipient. A typo in deadline_digest.de.html would fail here before
// any SMTP I/O.
//
// Also asserts that the rendered subject picks up the evening DRINGEND
// framing — the SLO-critical phrasing must survive the template-render
// path, not just the body.
func TestRenderTemplateDeadlineDigest(t *testing.T) {
	svc, err := NewMailService()
	if err != nil {
		t.Fatalf("NewMailService: %v", err)
	}
	subject, html, err := svc.RenderTemplate(TemplateData{
		Lang: "de",
		Name: "deadline_digest",
		Data: map[string]any{
			"Slot":            "evening",
			"IsEvening":       true,
			"OverdueCount":    0,
			"DueTodayCount":   1,
			"DueWarningCount": 0,
			"OpenTotal":       1,
			"DueToday": []map[string]any{
				{
					"DueDate":          "2026-04-28",
					"Title":            "Heute fällig",
					"ProjectReference": "2026/0002",
					"ProjectTitle":     "Acme v Gadget",
					"OwnerName":        "Self",
					"IsOtherOwner":     false,
					"URL":              "https://paliad.de/deadlines/b",
				},
			},
			"DeadlinesURL": "https://paliad.de/deadlines",
		},
	})
	if err != nil {
		t.Fatalf("RenderTemplate: %v", err)
	}
	wants := []string{
		"Paliad",
		"DRINGEND",                      // evening framing on the due_today section
		"Heute fällig",                  // row title (passed through as data)
		"2026/0002",
		"Acme v Gadget",
		"https://paliad.de/deadlines/b", // due_today link
		"https://paliad.de/deadlines",   // CTA
	}
	for _, want := range wants {
		if !strings.Contains(html, want) {
			t.Errorf("rendered html missing %q", want)
		}
	}
	// "Self" owner name should NOT appear because IsOtherOwner=false suppresses
	// the owner line.
	if strings.Contains(html, "Self") {
		t.Errorf("rendered html should not show OwnerName when IsOtherOwner=false: %q", html)
	}
	// The DE evening, no-overdue, due_today=1 path should render
	// "DRINGEND — 1 heute noch offen".
	wantSubject := "[Paliad] DRINGEND — 1 heute noch offen"
	if subject != wantSubject {
		t.Errorf("subject got %q, want %q", subject, wantSubject)
	}
}

// TestRenderTemplateDeadlineDigestSystemausfall covers the worst-case
// subject: evening slot with overdue rows. Must produce SYSTEMAUSFALL
// framing in DE (and SYSTEM FAILURE in EN — covered alongside).
func TestRenderTemplateDeadlineDigestSystemausfall(t *testing.T) {
	svc, _ := NewMailService()
	for _, tc := range []struct {
		name        string
		lang        string
		wantSubject string
	}{
		{"de evening overdue", "de", "[Paliad] SYSTEMAUSFALL: 2 überfällig — plus 1 heute offen"},
		{"en evening overdue", "en", "[Paliad] SYSTEM FAILURE: 2 overdue — plus 1 still open today"},
	} {
		t.Run(tc.name, func(t *testing.T) {
			subject, _, err := svc.RenderTemplate(TemplateData{
				Lang: tc.lang,
				Name: "deadline_digest",
				Data: map[string]any{
					"Slot":            "evening",
					"IsEvening":       true,
					"OverdueCount":    2,
					"DueTodayCount":   1,
					"DueWarningCount": 0,
					"OpenTotal":       1,
					"Overdue":         []map[string]any{{}, {}},
					"DueToday":        []map[string]any{{}},
					"DeadlinesURL":    "https://paliad.de/deadlines",
				},
			})
			if err != nil {
				t.Fatalf("RenderTemplate: %v", err)
			}
			if subject != tc.wantSubject {
				t.Errorf("subject got %q, want %q", subject, tc.wantSubject)
			}
		})
	}
}

// TestRenderTemplateInvitation covers the invitation template so a typo in
// invitation.en.html would fail CI.
func TestRenderTemplateInvitation(t *testing.T) {
	svc, err := NewMailService()
	if err != nil {
		t.Fatalf("NewMailService: %v", err)
	}
	subject, html, err := svc.RenderTemplate(TemplateData{
		Lang: "en",
		Name: "invitation",
		Data: map[string]any{
			"InviterName":  "Anna Schmidt",
			"InviterEmail": "anna@hlc.com",
			"ToEmail":      "colleague@hlc.com",
			"Message":      "Have a look at Paliad.",
			"RegisterURL":  "https://paliad.de/login",
		},
	})
	if err != nil {
		t.Fatalf("RenderTemplate: %v", err)
	}
	for _, want := range []string{
		"Anna Schmidt", "invites you", "Have a look at Paliad.",
		"https://paliad.de/login", "colleague@hlc.com",
		// Branding placeholder: {{.Firm}} should resolve to the configured
		// firm name (defaults to "HLC"). Catches accidental deletion of the
		// template placeholder when nobody set FIRM_NAME in the test env.
		"platform for HLC",
	} {
		if !strings.Contains(html, want) {
			t.Errorf("rendered html missing %q", want)
		}
	}
	if subject != "[Paliad] Anna Schmidt invites you to Paliad" {
		t.Errorf("subject got %q", subject)
	}
}

// TestBuildMIMEHasBothParts ensures the multipart/alternative structure
// carries both the text and HTML parts — an earlier refactor dropped one
// part by mistake, caught by this.
func TestBuildMIMEHasBothParts(t *testing.T) {
	msg := buildMIME("mail@paliad.de", "Paliad", "to@example.com",
		"Test", "<p>HTML</p>", "TEXT")
	body := string(msg)
	if !strings.Contains(body, "Content-Type: text/plain") {
		t.Error("missing text/plain part")
	}
	if !strings.Contains(body, "Content-Type: text/html") {
		t.Error("missing text/html part")
	}
	if !strings.Contains(body, "multipart/alternative") {
		t.Error("not multipart/alternative")
	}
	if !strings.Contains(body, "TEXT") {
		t.Error("text body missing")
	}
	if !strings.Contains(body, "<p>HTML</p>") {
		t.Error("html body missing")
	}
}
