package xrpc

import (
	"database/sql"
	"encoding/json"
	"errors"
	"net/http"
	"strings"

	"tangled.org/core/api/tangled"
	"tangled.org/core/appview/db"
	"tangled.org/core/appview/email"
	xrpcerr "tangled.org/core/xrpc/errors"
)

func (x *Xrpc) AccountListEmails(w http.ResponseWriter, r *http.Request) {
	l := x.Logger.With("handler", "AccountListEmails")

	did, ok := actorDid(r)
	if !ok {
		writeError(w, xrpcerr.MissingActorDidError, http.StatusForbidden)
		return
	}

	emails, err := db.GetAllEmails(x.DB, did)
	if err != nil {
		l.Error("failed to get emails", "err", err)
		writeError(w, errInternal, http.StatusInternalServerError)
		return
	}

	items := make([]*tangled.TempAccountListEmails_Email, 0, len(emails))
	for _, e := range emails {
		items = append(items, &tangled.TempAccountListEmails_Email{
			Address:   e.Address,
			Verified:  e.Verified,
			Primary:   e.Primary,
			CreatedAt: e.CreatedAt.UTC().Format(timeFormat),
		})
	}

	x.writeJSON(w, &tangled.TempAccountListEmails_Output{Emails: items})
}

func (x *Xrpc) AccountDeleteEmail(w http.ResponseWriter, r *http.Request) {
	l := x.Logger.With("handler", "AccountDeleteEmail")

	did, ok := actorDid(r)
	if !ok {
		writeError(w, xrpcerr.MissingActorDidError, http.StatusForbidden)
		return
	}

	var input tangled.TempAccountDeleteEmail_Input
	if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
		writeError(w, errBadRequestBody, http.StatusBadRequest)
		return
	}
	addr := strings.TrimSpace(input.Email)

	existing, err := db.GetEmail(x.DB, did, addr)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			writeError(w, xrpcErrorTag("EmailNotFound", "The email address is not associated with this account."), http.StatusNotFound)
			return
		}
		l.Error("failed to get email", "err", err)
		writeError(w, errInternal, http.StatusInternalServerError)
		return
	}
	if existing.Primary {
		writeError(w, xrpcErrorTag("CannotDeletePrimary", "The primary email address cannot be deleted; set another address as primary first."), http.StatusBadRequest)
		return
	}

	if err := db.DeleteEmail(x.DB, did, addr); err != nil {
		l.Error("failed to delete email", "err", err)
		writeError(w, errInternal, http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
}

func (x *Xrpc) AccountSetPrimaryEmail(w http.ResponseWriter, r *http.Request) {
	l := x.Logger.With("handler", "AccountSetPrimaryEmail")

	did, ok := actorDid(r)
	if !ok {
		writeError(w, xrpcerr.MissingActorDidError, http.StatusForbidden)
		return
	}

	var input tangled.TempAccountSetPrimaryEmail_Input
	if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
		writeError(w, errBadRequestBody, http.StatusBadRequest)
		return
	}
	addr := strings.TrimSpace(input.Email)

	existing, err := db.GetEmail(x.DB, did, addr)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			writeError(w, xrpcErrorTag("EmailNotFound", "The email address is not associated with this account."), http.StatusNotFound)
			return
		}
		l.Error("failed to get email", "err", err)
		writeError(w, errInternal, http.StatusInternalServerError)
		return
	}
	if !existing.Verified {
		writeError(w, xrpcErrorTag("EmailNotVerified", "The email address must be verified before it can be made primary."), http.StatusBadRequest)
		return
	}

	if err := db.MakeEmailPrimary(x.DB, did, addr); err != nil {
		l.Error("failed to set primary email", "err", err)
		writeError(w, errInternal, http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
}

func (x *Xrpc) AccountSubscribeNewsletter(w http.ResponseWriter, r *http.Request) {
	l := x.Logger.With("handler", "AccountSubscribeNewsletter")

	did, ok := actorDid(r)
	if !ok {
		writeError(w, xrpcerr.MissingActorDidError, http.StatusForbidden)
		return
	}

	primary, err := db.GetPrimaryEmail(x.DB, did)
	if err != nil || primary.Address == "" {
		writeError(w, xrpcErrorTag("NoVerifiedEmail", "A primary email address is required to subscribe."), http.StatusBadRequest)
		return
	}

	if err := db.UpsertNewsletterPref(x.DB, did, db.NewsletterStatusSubscribed, primary.Address); err != nil {
		l.Error("failed to persist newsletter preference", "err", err)
		writeError(w, errInternal, http.StatusInternalServerError)
		return
	}

	if x.Config.Resend.ApiKey != "" && x.Config.Resend.NewsletterSegmentId != "" {
		go func() {
			if err := email.AddNewsletterContact(x.Config.Resend.ApiKey, x.Config.Resend.NewsletterSegmentId, primary.Address); err != nil {
				l.Error("failed to add newsletter contact", "err", err)
			}
		}()
	}

	w.WriteHeader(http.StatusOK)
}

func (x *Xrpc) AccountDismissNewsletter(w http.ResponseWriter, r *http.Request) {
	l := x.Logger.With("handler", "AccountDismissNewsletter")

	did, ok := actorDid(r)
	if !ok {
		writeError(w, xrpcerr.MissingActorDidError, http.StatusForbidden)
		return
	}

	if err := db.UpsertNewsletterPref(x.DB, did, db.NewsletterStatusDismissed, ""); err != nil {
		l.Error("failed to persist newsletter dismissal", "err", err)
		writeError(w, errInternal, http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
}
