package webui

import (
	"net/http"
	"net/http/httptest"
	"net/url"
	"strconv"
	"strings"
	"testing"
)

// TestWebmailMessagePaneMarksReadAndReturnsFragment confirms the Outlook-style reading
// pane's fetch endpoint returns a bare fragment (no dashboard chrome) containing the
// message body, and marks the message read just like opening the full page does.
func TestWebmailMessagePaneMarksReadAndReturnsFragment(t *testing.T) {
	app := newTestApp(t)
	mux := app.Mux()
	domains, _ := app.DB.ListDomains()
	mailboxID := createTestMailboxWithPassword(t, app, "panetest@example.com", domains[0].ID, "panetest-password-1!")
	cookie := webmailLoginSession(t, app, mailboxID)

	uid := storeTestMessage(t, app, mailboxID, "INBOX", "a@example.com", "Pane test", "the body of the message")

	req := httptest.NewRequest(http.MethodGet, MailboxPrefix+"/mail/INBOX/"+strconv.FormatInt(uid, 10)+"/pane", nil)
	req.AddCookie(cookie)
	rec := httptest.NewRecorder()
	mux.ServeHTTP(rec, req)
	if rec.Code != http.StatusOK {
		t.Fatalf("expected 200, got %d: %s", rec.Code, rec.Body.String())
	}
	body := rec.Body.String()
	if !strings.Contains(body, "the body of the message") {
		t.Errorf("expected the message body in the fragment, got:\n%s", body)
	}
	if strings.Contains(body, "<!DOCTYPE") || strings.Contains(body, "navbar-brand") {
		t.Errorf("expected a bare fragment with no page chrome, got:\n%s", body)
	}

	msg, err := app.DB.GetMessageByUID(mailboxID, uid)
	if err != nil {
		t.Fatal(err)
	}
	if isUnread(msg.Flags) {
		t.Error("expected the message marked read after loading it in the pane")
	}
}

// TestWebmailBulkActionDeleteAndMarkRead confirms the folder view's bulk toolbar
// (multi-select checkboxes -> POST .../bulk) can delete-to-Trash and mark-read/unread
// several messages in one request.
func TestWebmailBulkActionDeleteAndMarkRead(t *testing.T) {
	app := newTestApp(t)
	mux := app.Mux()
	domains, _ := app.DB.ListDomains()
	mailboxID := createTestMailboxWithPassword(t, app, "bulktest@example.com", domains[0].ID, "bulktest-password-1!")
	cookie := webmailLoginSession(t, app, mailboxID)

	uid1 := storeTestMessage(t, app, mailboxID, "INBOX", "a@example.com", "One", "body1")
	uid2 := storeTestMessage(t, app, mailboxID, "INBOX", "b@example.com", "Two", "body2")
	uid3 := storeTestMessage(t, app, mailboxID, "INBOX", "c@example.com", "Three", "body3")

	post := func(action string, uids ...int64) *httptest.ResponseRecorder {
		form := url.Values{"action": {action}}
		for _, u := range uids {
			form.Add("uid", strconv.FormatInt(u, 10))
		}
		req := httptest.NewRequest(http.MethodPost, MailboxPrefix+"/mail/INBOX/bulk", strings.NewReader(form.Encode()))
		req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
		req.AddCookie(cookie)
		rec := httptest.NewRecorder()
		mux.ServeHTTP(rec, req)
		return rec
	}

	// Mark uid1 and uid2 read in one bulk request.
	if rec := post("read", uid1, uid2); rec.Code != http.StatusFound {
		t.Fatalf("bulk read: status=%d body=%s", rec.Code, rec.Body.String())
	}
	for _, uid := range []int64{uid1, uid2} {
		msg, err := app.DB.GetMessageByUID(mailboxID, uid)
		if err != nil {
			t.Fatal(err)
		}
		if isUnread(msg.Flags) {
			t.Errorf("expected message %d marked read", uid)
		}
	}
	msg3, err := app.DB.GetMessageByUID(mailboxID, uid3)
	if err != nil {
		t.Fatal(err)
	}
	if !isUnread(msg3.Flags) {
		t.Error("expected message 3 (not in the bulk request) to remain unread")
	}

	// Bulk-delete uid1 and uid3 (uid2 stays in INBOX).
	if rec := post("delete", uid1, uid3); rec.Code != http.StatusFound {
		t.Fatalf("bulk delete: status=%d body=%s", rec.Code, rec.Body.String())
	}
	inbox, err := app.DB.ListMessagesInFolder(mailboxID, "INBOX")
	if err != nil {
		t.Fatal(err)
	}
	if len(inbox) != 1 || inbox[0].ID != uid2 {
		t.Fatalf("expected only message 2 left in INBOX, got %+v", inbox)
	}
	trash, err := app.DB.ListMessagesInFolder(mailboxID, "Trash")
	if err != nil {
		t.Fatal(err)
	}
	if len(trash) != 2 {
		t.Fatalf("expected 2 messages in Trash, got %d", len(trash))
	}
}

// TestWebmailBulkActionSkipsUIDFromAnotherFolder confirms a uid that doesn't actually
// belong to the requested folder is silently skipped rather than aborting the whole
// batch or letting a stale/mismatched selection touch the wrong message.
func TestWebmailBulkActionSkipsUIDFromAnotherFolder(t *testing.T) {
	app := newTestApp(t)
	mux := app.Mux()
	domains, _ := app.DB.ListDomains()
	mailboxID := createTestMailboxWithPassword(t, app, "bulkskiptest@example.com", domains[0].ID, "bulkskiptest-password-1!")
	cookie := webmailLoginSession(t, app, mailboxID)

	inboxUID := storeTestMessage(t, app, mailboxID, "INBOX", "a@example.com", "In inbox", "body")
	sentUID := storeTestMessage(t, app, mailboxID, "Sent", "b@example.com", "In sent", "body")

	form := url.Values{"action": {"delete"}, "uid": {strconv.FormatInt(inboxUID, 10), strconv.FormatInt(sentUID, 10)}}
	req := httptest.NewRequest(http.MethodPost, MailboxPrefix+"/mail/INBOX/bulk", strings.NewReader(form.Encode()))
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	req.AddCookie(cookie)
	rec := httptest.NewRecorder()
	mux.ServeHTTP(rec, req)
	if rec.Code != http.StatusFound {
		t.Fatalf("status=%d body=%s", rec.Code, rec.Body.String())
	}

	sent, err := app.DB.ListMessagesInFolder(mailboxID, "Sent")
	if err != nil {
		t.Fatal(err)
	}
	if len(sent) != 1 {
		t.Fatalf("expected the Sent message untouched (wrong folder for this bulk request), got %d left", len(sent))
	}
	trash, err := app.DB.ListMessagesInFolder(mailboxID, "Trash")
	if err != nil {
		t.Fatal(err)
	}
	if len(trash) != 1 {
		t.Fatalf("expected the INBOX message moved to Trash, got %d in Trash", len(trash))
	}
}
