package mailview

import (
	"strings"
	"testing"
)

func TestParseSimpleTextMessage(t *testing.T) {
	raw := "From: a@example.com\r\nTo: b@example.com\r\nSubject: hi\r\n\r\nhello there"
	m, err := Parse([]byte(raw))
	if err != nil {
		t.Fatal(err)
	}
	if m.Header.From != "a@example.com" || m.Header.Subject != "hi" {
		t.Errorf("headers = %+v", m.Header)
	}
	if m.TextBody != "hello there" {
		t.Errorf("TextBody = %q", m.TextBody)
	}
	if m.HTMLBody != "" || len(m.Attachments) != 0 {
		t.Errorf("expected no HTML body or attachments, got HTMLBody=%q attachments=%d", m.HTMLBody, len(m.Attachments))
	}
}

func TestParseMultipartAlternativeKeepsBothBodies(t *testing.T) {
	raw := "" +
		"From: a@example.com\r\nTo: b@example.com\r\nSubject: hi\r\n" +
		"Content-Type: multipart/alternative; boundary=\"B\"\r\n\r\n" +
		"--B\r\nContent-Type: text/plain\r\n\r\nplain version\r\n" +
		"--B\r\nContent-Type: text/html\r\n\r\n<p>html version</p>\r\n" +
		"--B--\r\n"
	m, err := Parse([]byte(raw))
	if err != nil {
		t.Fatal(err)
	}
	if strings.TrimSpace(m.TextBody) != "plain version" {
		t.Errorf("TextBody = %q", m.TextBody)
	}
	if strings.TrimSpace(m.HTMLBody) != "<p>html version</p>" {
		t.Errorf("HTMLBody = %q", m.HTMLBody)
	}
}

func TestParseAttachmentDecodesBase64(t *testing.T) {
	raw := "" +
		"From: a@example.com\r\nTo: b@example.com\r\nSubject: hi\r\n" +
		"Content-Type: multipart/mixed; boundary=\"B\"\r\n\r\n" +
		"--B\r\nContent-Type: text/plain\r\n\r\nsee attached\r\n" +
		"--B\r\nContent-Type: application/octet-stream\r\nContent-Disposition: attachment; filename=\"a.txt\"\r\n" +
		"Content-Transfer-Encoding: BASE64\r\n\r\nSGVsbG8sIHdvcmxkIQ==\r\n" +
		"--B--\r\n"
	m, err := Parse([]byte(raw))
	if err != nil {
		t.Fatal(err)
	}
	if len(m.Attachments) != 1 {
		t.Fatalf("got %d attachments, want 1", len(m.Attachments))
	}
	if got := string(m.Attachments[0].Data); got != "Hello, world!" {
		t.Errorf("attachment data = %q, want decoded base64", got)
	}
	if m.Attachments[0].Filename != "a.txt" {
		t.Errorf("filename = %q", m.Attachments[0].Filename)
	}
}

func TestParseNestedMultipartMixedWithAlternativeBody(t *testing.T) {
	raw := "" +
		"From: a@example.com\r\nTo: b@example.com\r\nSubject: hi\r\n" +
		"Content-Type: multipart/mixed; boundary=\"OUTER\"\r\n\r\n" +
		"--OUTER\r\nContent-Type: multipart/alternative; boundary=\"INNER\"\r\n\r\n" +
		"--INNER\r\nContent-Type: text/plain\r\n\r\nplain body\r\n" +
		"--INNER\r\nContent-Type: text/html\r\n\r\n<p>html body</p>\r\n" +
		"--INNER--\r\n" +
		"--OUTER\r\nContent-Type: text/plain\r\nContent-Disposition: attachment; filename=\"notes.txt\"\r\n\r\n" +
		"attached notes\r\n" +
		"--OUTER--\r\n"
	m, err := Parse([]byte(raw))
	if err != nil {
		t.Fatal(err)
	}
	if strings.TrimSpace(m.TextBody) != "plain body" {
		t.Errorf("TextBody = %q", m.TextBody)
	}
	if strings.TrimSpace(m.HTMLBody) != "<p>html body</p>" {
		t.Errorf("HTMLBody = %q", m.HTMLBody)
	}
	if len(m.Attachments) != 1 || m.Attachments[0].Filename != "notes.txt" {
		t.Fatalf("attachments = %+v", m.Attachments)
	}
}

func TestParseInlineImageCapturesContentID(t *testing.T) {
	raw := "" +
		"From: a@example.com\r\nTo: b@example.com\r\nSubject: hi\r\n" +
		"Content-Type: multipart/related; boundary=\"B\"\r\n\r\n" +
		"--B\r\nContent-Type: text/html\r\n\r\n<p><img src=\"cid:img1@example.com\"></p>\r\n" +
		"--B\r\nContent-Type: image/png\r\nContent-Disposition: inline; filename=\"header.png\"\r\n" +
		"Content-Id: <img1@example.com>\r\n" +
		"Content-Transfer-Encoding: BASE64\r\n\r\nSGVsbG8sIHdvcmxkIQ==\r\n" +
		"--B--\r\n"
	m, err := Parse([]byte(raw))
	if err != nil {
		t.Fatal(err)
	}
	if strings.TrimSpace(m.HTMLBody) != `<p><img src="cid:img1@example.com"></p>` {
		t.Errorf("HTMLBody = %q", m.HTMLBody)
	}
	if len(m.Attachments) != 1 {
		t.Fatalf("attachments = %+v", m.Attachments)
	}
	if m.Attachments[0].ContentID != "img1@example.com" {
		t.Errorf("ContentID = %q, want angle brackets stripped", m.Attachments[0].ContentID)
	}
	if got := string(m.Attachments[0].Data); got != "Hello, world!" {
		t.Errorf("attachment data = %q, want decoded base64", got)
	}
}

func TestParseInlineImageWithNoFilenameStillCaptured(t *testing.T) {
	raw := "" +
		"From: a@example.com\r\nTo: b@example.com\r\nSubject: hi\r\n" +
		"Content-Type: multipart/related; boundary=\"B\"\r\n\r\n" +
		"--B\r\nContent-Type: text/html\r\n\r\n<p>hi</p>\r\n" +
		"--B\r\nContent-Type: image/png\r\n" +
		"Content-Id: <noname@example.com>\r\n\r\nrawbytes" +
		"\r\n--B--\r\n"
	m, err := Parse([]byte(raw))
	if err != nil {
		t.Fatal(err)
	}
	// No filename anywhere on the part — must not be silently dropped just because
	// there's nothing to name it; falls back to the Content-Id itself.
	if len(m.Attachments) != 1 || m.Attachments[0].ContentID != "noname@example.com" {
		t.Fatalf("attachments = %+v", m.Attachments)
	}
}
