package webui

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

// TestWebmailContactCreateEditDelete exercises the full self-service contact CRUD
// flow through the add/edit popup's shared save endpoint.
func TestWebmailContactCreateEditDelete(t *testing.T) {
	app := newTestApp(t)
	mux := app.Mux()
	domains, _ := app.DB.ListDomains()
	mailboxID := createTestMailboxWithPassword(t, app, "contactowner@example.com", domains[0].ID, "contact-password-1!")
	cookie := webmailLoginSession(t, app, mailboxID)

	save := func(id, email, name, phone string) *httptest.ResponseRecorder {
		form := url.Values{"id": {id}, "email": {email}, "name": {name}, "phone": {phone}}
		req := httptest.NewRequest(http.MethodPost, MailboxPrefix+"/contacts/save", 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
	}

	if rec := save("", "jane@example.com", "Jane Doe", "555-1234"); rec.Code != http.StatusFound {
		t.Fatalf("create: status=%d body=%s", rec.Code, rec.Body.String())
	}
	contacts, err := app.DB.ListContacts(mailboxID)
	if err != nil || len(contacts) != 1 || contacts[0].Name != "Jane Doe" {
		t.Fatalf("expected 1 contact named Jane Doe, got %+v (err=%v)", contacts, err)
	}
	id := contacts[0].ID

	if rec := save(strconv.FormatInt(id, 10), "jane@example.com", "Jane D.", ""); rec.Code != http.StatusFound {
		t.Fatalf("edit: status=%d body=%s", rec.Code, rec.Body.String())
	}
	updated, err := app.DB.GetContactByID(mailboxID, id)
	if err != nil || updated == nil || updated.Name != "Jane D." || updated.Phone != "" {
		t.Fatalf("expected updated contact, got %+v (err=%v)", updated, err)
	}

	// Missing name/email is rejected rather than silently stored.
	if rec := save("", "", "No Email", ""); rec.Code != http.StatusFound {
		t.Fatalf("status=%d", rec.Code)
	}
	contacts, err = app.DB.ListContacts(mailboxID)
	if err != nil || len(contacts) != 1 {
		t.Fatalf("expected the invalid contact rejected (still 1), got %d (err=%v)", len(contacts), err)
	}

	delReq := httptest.NewRequest(http.MethodPost, MailboxPrefix+"/contacts/"+strconv.FormatInt(id, 10)+"/delete", nil)
	delReq.AddCookie(cookie)
	delRec := httptest.NewRecorder()
	mux.ServeHTTP(delRec, delReq)
	if delRec.Code != http.StatusFound {
		t.Fatalf("delete: status=%d body=%s", delRec.Code, delRec.Body.String())
	}
	remaining, err := app.DB.ListContacts(mailboxID)
	if err != nil || len(remaining) != 0 {
		t.Fatalf("expected no contacts left, got %+v (err=%v)", remaining, err)
	}
}

// TestWebmailContactScopedToOwnMailbox confirms one mailbox owner can't delete
// another mailbox's contact by guessing its ID.
func TestWebmailContactScopedToOwnMailbox(t *testing.T) {
	app := newTestApp(t)
	mux := app.Mux()
	domains, _ := app.DB.ListDomains()
	victimID := createTestMailboxWithPassword(t, app, "victim4@example.com", domains[0].ID, "victim-password-1!")
	attackerID := createTestMailboxWithPassword(t, app, "attacker4@example.com", domains[0].ID, "attacker-password-1!")

	contactID, err := app.DB.CreateContact(victimID, "friend@example.com", "Friend", "")
	if err != nil {
		t.Fatal(err)
	}

	attackerCookie := webmailLoginSession(t, app, attackerID)
	req := httptest.NewRequest(http.MethodPost, MailboxPrefix+"/contacts/"+strconv.FormatInt(contactID, 10)+"/delete", nil)
	req.AddCookie(attackerCookie)
	rec := httptest.NewRecorder()
	mux.ServeHTTP(rec, req)
	if rec.Code != http.StatusFound {
		t.Fatalf("status=%d", rec.Code)
	}

	stillThere, err := app.DB.GetContactByID(victimID, contactID)
	if err != nil || stillThere == nil {
		t.Fatalf("expected the victim's contact untouched, got %+v (err=%v)", stillThere, err)
	}
}
