package webui

import (
	"io"
	"net/http"
	"strings"

	"mailgoserver/internal/smime"
)

// webmailCertsPage shows both of a mailbox owner's certificate/key types: S/MIME
// certificates (generate/import/remove/download, used for signing, stored plain)
// and PGP keys (used for encryption — see webmail_pgp.go — each protected by its
// own passphrase), plus their collected contact certificates/keys for each
// protocol.
func (a *App) webmailCertsPage(w http.ResponseWriter, r *http.Request) {
	mbox := mailboxFromContext(r)
	token := sessionToken(r)

	identities, err := a.DB.ListSMIMEIdentities(mbox.ID)
	if err != nil {
		setFlash(w, "error", "Error loading your S/MIME certificates")
	}
	contacts, err := a.DB.ListSMIMEContacts(mbox.ID)
	if err != nil {
		setFlash(w, "error", "Error loading S/MIME contact certificates")
	}

	pgpIdentities, err := a.DB.ListPGPIdentities(mbox.ID)
	if err != nil {
		setFlash(w, "error", "Error loading your PGP keys")
	}
	pgpContacts, err := a.DB.ListPGPContacts(mbox.ID)
	if err != nil {
		setFlash(w, "error", "Error loading PGP contact keys")
	}
	pgpUnlocked := make(map[int64]bool, len(pgpIdentities))
	for _, id := range pgpIdentities {
		if _, ok := a.pgpKeys.get(token, id.ID); ok {
			pgpUnlocked[id.ID] = true
		}
	}

	a.render(w, r, "webmail_certs.html", M{
		"mailbox":        mbox,
		"identities":     identities,
		"contacts":       contacts,
		"pgp_identities": pgpIdentities,
		"pgp_unlocked":   pgpUnlocked,
		"pgp_contacts":   pgpContacts,
		"flashes":        popFlashes(w, r),
		"active_section": "certs",
	})
}

// storeIdentity adds a new identity with certPEM/keyPEM stored as-is — no
// passphrase wrapping (see the schema comment on esrv_mailbox_smime_identities).
func (a *App) storeIdentity(mailboxID int64, certPEM, keyPEM []byte) error {
	cert, err := smime.ParseCertPEM(certPEM)
	if err != nil {
		return err
	}
	_, err = a.DB.CreateSMIMEIdentity(mailboxID, string(certPEM), string(keyPEM), cert.NotAfter)
	return err
}

func (a *App) webmailSMIMEGenerate(w http.ResponseWriter, r *http.Request) {
	mbox := mailboxFromContext(r)
	certPEM, keyPEM, err := smime.GenerateSelfSigned(mbox.Email, smime.DefaultValidity)
	if err == nil {
		err = a.storeIdentity(mbox.ID, certPEM, keyPEM)
	}
	if err != nil {
		a.Logger.Error("smime generate for mailbox %d: %v", mbox.ID, err)
		setFlash(w, "error", "Error generating certificate")
	} else {
		setFlash(w, "success", "S/MIME certificate generated")
	}
	http.Redirect(w, r, MailboxPrefix+"/certs", http.StatusFound)
}

func (a *App) webmailSMIMEImport(w http.ResponseWriter, r *http.Request) {
	mbox := mailboxFromContext(r)
	if err := r.ParseMultipartForm(1 << 20); err != nil {
		setFlash(w, "error", "Error reading upload")
		http.Redirect(w, r, MailboxPrefix+"/certs", http.StatusFound)
		return
	}
	file, _, err := r.FormFile("p12_file")
	if err != nil {
		setFlash(w, "error", "Please choose a .p12/.pfx file")
		http.Redirect(w, r, MailboxPrefix+"/certs", http.StatusFound)
		return
	}
	defer file.Close()
	data, err := io.ReadAll(file)
	if err == nil {
		var certPEM, keyPEM []byte
		// p12_password unlocks the uploaded .p12 bundle itself, a one-time-use secret
		// distinct from anything stored afterward — the key is kept plain from here on.
		certPEM, keyPEM, err = smime.ImportPKCS12(data, r.FormValue("p12_password"))
		if err == nil {
			err = a.storeIdentity(mbox.ID, certPEM, keyPEM)
		}
	}
	if err != nil {
		setFlash(w, "error", "Error importing certificate: "+err.Error())
	} else {
		setFlash(w, "success", "S/MIME certificate imported")
	}
	http.Redirect(w, r, MailboxPrefix+"/certs", http.StatusFound)
}

func (a *App) webmailSMIMERemoveIdentity(w http.ResponseWriter, r *http.Request) {
	mbox := mailboxFromContext(r)
	identityID := int64(atoi(r.PathValue("identity_id")))
	if err := a.DB.DeleteSMIMEIdentity(mbox.ID, identityID); err != nil {
		setFlash(w, "error", "Error removing identity")
	} else {
		setFlash(w, "success", "S/MIME identity removed")
	}
	http.Redirect(w, r, MailboxPrefix+"/certs", http.StatusFound)
}

func (a *App) webmailSMIMEDownloadCert(w http.ResponseWriter, r *http.Request) {
	mbox := mailboxFromContext(r)
	identityID := int64(atoi(r.PathValue("identity_id")))
	identity, err := a.DB.GetSMIMEIdentity(mbox.ID, identityID)
	if err != nil || identity == nil {
		http.NotFound(w, r)
		return
	}
	w.Header().Set("Content-Type", "application/x-x509-user-cert")
	w.Header().Set("Content-Disposition", `attachment; filename="`+mbox.Email+`.crt"`)
	w.Write([]byte(identity.CertPEM))
}

func (a *App) webmailSMIMEAddContact(w http.ResponseWriter, r *http.Request) {
	mbox := mailboxFromContext(r)
	if err := r.ParseMultipartForm(1 << 20); err != nil {
		setFlash(w, "error", "Error reading upload")
		http.Redirect(w, r, MailboxPrefix+"/certs", http.StatusFound)
		return
	}
	email := strings.TrimSpace(r.FormValue("email"))
	file, _, err := r.FormFile("cert_file")
	if email == "" || err != nil {
		setFlash(w, "error", "Please provide an email and a certificate file")
		http.Redirect(w, r, MailboxPrefix+"/certs", http.StatusFound)
		return
	}
	defer file.Close()
	data, err := io.ReadAll(file)
	if err == nil {
		_, err = smime.ParseCertPEM(data)
	}
	if err != nil {
		setFlash(w, "error", "That doesn't look like a valid certificate file")
		http.Redirect(w, r, MailboxPrefix+"/certs", http.StatusFound)
		return
	}
	if err := a.DB.UpsertSMIMEContact(mbox.ID, email, string(data)); err != nil {
		setFlash(w, "error", "Error saving contact certificate")
	} else {
		setFlash(w, "success", "Contact certificate added")
	}
	http.Redirect(w, r, MailboxPrefix+"/certs", http.StatusFound)
}

func (a *App) webmailSMIMERemoveContact(w http.ResponseWriter, r *http.Request) {
	mbox := mailboxFromContext(r)
	contactID := int64(atoi(r.PathValue("contact_id")))
	if err := a.DB.DeleteSMIMEContact(mbox.ID, contactID); err != nil {
		setFlash(w, "error", "Error removing contact")
	} else {
		setFlash(w, "success", "Contact removed")
	}
	http.Redirect(w, r, MailboxPrefix+"/certs", http.StatusFound)
}
