package webui

import (
	"net/http"
	"strconv"
	"strings"

	"github.com/pquerna/otp/totp"
	"mailgoserver/internal/db"
)

// mailboxPendingMFACookieName mirrors pendingMFACookieName for the mailbox portal —
// kept fully separate so an unfinished mailbox login can never be confused with (or
// promoted into) an admin session, and vice versa.
const mailboxPendingMFACookieName = "mailgoserver_mailbox_pending_mfa"

func setMailboxPendingMFACookie(w http.ResponseWriter, mailboxID string) {
	http.SetCookie(w, &http.Cookie{
		Name: mailboxPendingMFACookieName, Value: mailboxID, Path: "/", HttpOnly: true,
		SameSite: http.SameSiteLaxMode, MaxAge: 10 * 60,
	})
}
func clearMailboxPendingMFACookie(w http.ResponseWriter) {
	http.SetCookie(w, &http.Cookie{Name: mailboxPendingMFACookieName, Value: "", Path: "/", MaxAge: -1})
}
func pendingMailboxMFAID(r *http.Request) int64 {
	c, err := r.Cookie(mailboxPendingMFACookieName)
	if err != nil {
		return 0
	}
	return int64(atoi(c.Value))
}

func (a *App) webmailLoginForm(w http.ResponseWriter, r *http.Request) {
	if sess, mbox, _ := a.currentMailboxSession(r); sess != nil && mbox != nil {
		http.Redirect(w, r, MailboxPrefix+"/", http.StatusFound)
		return
	}
	a.render(w, r, "webmail_login.html", M{})
}

// webmailLoginSubmit checks email+password against the mailbox's own portal
// password (never an app password — that's for IMAP/SMTP clients only).
func (a *App) webmailLoginSubmit(w http.ResponseWriter, r *http.Request) {
	if !a.rateLimitLogin(w, r) {
		return
	}
	email := strings.TrimSpace(r.FormValue("email"))
	password := r.FormValue("password")

	fail := func(msg string) {
		a.render(w, r, "webmail_login.html", M{"error": msg, "email": email})
	}

	if a.accountLocked("webmail_login", email) {
		fail("Too many failed attempts for this account. Try again later.")
		return
	}

	mbox, err := a.DB.GetMailboxByEmail(email)
	if err != nil {
		a.Logger.Error("webmail login lookup: %v", err)
		fail("Something went wrong. Try again.")
		return
	}
	if mbox == nil || !db.CheckPassword(password, mbox.PasswordHash) {
		_ = a.DB.LogAuthAttempt("webmail_login", email, a.requestIP(r), false, "Incorrect email or password")
		fail("Incorrect email or password.")
		return
	}

	needsMFA := mbox.TOTPEnabled
	if !needsMFA {
		if n, _ := a.DB.CountMailboxWebAuthnCredentials(mbox.ID); n > 0 {
			needsMFA = true
		}
	}

	if !needsMFA {
		token, err := a.DB.CreateMailboxSession(mbox.ID, true, sessionTTL)
		if err != nil {
			fail("Something went wrong. Try again.")
			return
		}
		_ = a.DB.LogAuthAttempt("webmail_login", email, a.requestIP(r), true, "Login successful")
		setMailboxSessionCookie(w, token, r.TLS != nil)
		http.Redirect(w, r, MailboxPrefix+"/", http.StatusFound)
		return
	}

	setMailboxPendingMFACookie(w, strconv.FormatInt(mbox.ID, 10))
	http.Redirect(w, r, MailboxPrefix+"/login/mfa", http.StatusFound)
}

func (a *App) webmailMFAForm(w http.ResponseWriter, r *http.Request) {
	mailboxID := pendingMailboxMFAID(r)
	if mailboxID == 0 {
		http.Redirect(w, r, MailboxPrefix+"/login", http.StatusFound)
		return
	}
	mbox, _ := a.DB.GetMailboxByID(mailboxID)
	if mbox == nil {
		clearMailboxPendingMFACookie(w)
		http.Redirect(w, r, MailboxPrefix+"/login", http.StatusFound)
		return
	}
	hasPasskeys, _ := a.DB.CountMailboxWebAuthnCredentials(mailboxID)
	a.render(w, r, "webmail_login_mfa.html", M{"totp_enabled": mbox.TOTPEnabled, "has_passkeys": hasPasskeys > 0})
}

func (a *App) webmailMFASubmit(w http.ResponseWriter, r *http.Request) {
	if !a.rateLimitLogin(w, r) {
		return
	}
	mailboxID := pendingMailboxMFAID(r)
	if mailboxID == 0 {
		http.Redirect(w, r, MailboxPrefix+"/login", http.StatusFound)
		return
	}
	mbox, err := a.DB.GetMailboxByID(mailboxID)
	if err != nil || mbox == nil {
		clearMailboxPendingMFACookie(w)
		http.Redirect(w, r, MailboxPrefix+"/login", http.StatusFound)
		return
	}

	code := strings.TrimSpace(r.FormValue("code"))
	if !mbox.TOTPEnabled || !totp.Validate(code, mbox.TOTPSecret) {
		_ = a.DB.LogAuthAttempt("webmail_login", mbox.Email, a.requestIP(r), false, "Invalid MFA code")
		hasPasskeys, _ := a.DB.CountMailboxWebAuthnCredentials(mailboxID)
		a.render(w, r, "webmail_login_mfa.html", M{"totp_enabled": mbox.TOTPEnabled, "has_passkeys": hasPasskeys > 0, "error": "Invalid code."})
		return
	}

	token, err := a.DB.CreateMailboxSession(mbox.ID, true, sessionTTL)
	if err != nil {
		a.Logger.Error("create mailbox session: %v", err)
		http.Redirect(w, r, MailboxPrefix+"/login", http.StatusFound)
		return
	}
	_ = a.DB.LogAuthAttempt("webmail_login", mbox.Email, a.requestIP(r), true, "Login successful (authenticator app)")
	clearMailboxPendingMFACookie(w)
	setMailboxSessionCookie(w, token, r.TLS != nil)
	http.Redirect(w, r, MailboxPrefix+"/", http.StatusFound)
}

func (a *App) webmailLogout(w http.ResponseWriter, r *http.Request) {
	if c, err := r.Cookie(mailboxSessionCookieName); err == nil {
		_ = a.DB.DeleteMailboxSession(c.Value)
		a.pgpKeys.clearSession(c.Value)
	}
	clearMailboxSessionCookie(w)
	http.Redirect(w, r, MailboxPrefix+"/login", http.StatusFound)
}
