package api

import (
	"context"
	"encoding/json"
	"errors"
	"net/http"
	"time"

	"git.fabledsword.com/bvandeusen/minstrel/internal/apierror"
	"git.fabledsword.com/bvandeusen/minstrel/internal/db/dbq"
	"git.fabledsword.com/bvandeusen/minstrel/internal/mailer"
)

type smtpConfigResp struct {
	Enabled     bool   `json:"enabled"`
	Host        string `json:"host"`
	Port        int32  `json:"port"`
	Username    string `json:"username"`
	Password    string `json:"password"` // masked: "***" if non-empty, "" otherwise
	FromAddress string `json:"from_address"`
	FromName    string `json:"from_name"`
	UseTLS      bool   `json:"use_tls"`
}

type updateSMTPConfigReq struct {
	Enabled     bool   `json:"enabled"`
	Host        string `json:"host"`
	Port        int32  `json:"port"`
	Username    string `json:"username"`
	Password    string `json:"password"` // empty string = preserve existing
	FromAddress string `json:"from_address"`
	FromName    string `json:"from_name"`
	UseTLS      bool   `json:"use_tls"`
}

func (h *handlers) handleGetSMTPConfig(w http.ResponseWriter, r *http.Request) {
	q := dbq.New(h.pool)
	cfg, err := q.GetSMTPConfig(r.Context())
	if err != nil {
		writeErrWithLog(w, h.logger, "admin smtp: get failed", apierror.Internal(err))
		return
	}
	masked := ""
	if cfg.Password != "" {
		masked = "***"
	}
	writeJSON(w, http.StatusOK, smtpConfigResp{
		Enabled:     cfg.Enabled,
		Host:        cfg.Host,
		Port:        cfg.Port,
		Username:    cfg.Username,
		Password:    masked,
		FromAddress: cfg.FromAddress,
		FromName:    cfg.FromName,
		UseTLS:      cfg.UseTls,
	})
}

func (h *handlers) handleUpdateSMTPConfig(w http.ResponseWriter, r *http.Request) {
	var req updateSMTPConfigReq
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeErr(w, apierror.BadRequest("invalid_body", ""))
		return
	}
	if req.Enabled {
		if req.Host == "" || req.FromAddress == "" {
			writeErr(w, apierror.BadRequest("missing_fields",
				"host and from_address are required when enabled"))
			return
		}
	}
	q := dbq.New(h.pool)
	current, err := q.GetSMTPConfig(r.Context())
	if err != nil {
		writeErrWithLog(w, h.logger, "admin smtp: get failed", apierror.Internal(err))
		return
	}
	// Preserve existing password when the request sends "" (so the
	// admin doesn't have to re-enter it on every save).
	password := req.Password
	if password == "" {
		password = current.Password
	}
	if err := q.UpdateSMTPConfig(r.Context(), dbq.UpdateSMTPConfigParams{
		Enabled:     req.Enabled,
		Host:        req.Host,
		Port:        req.Port,
		Username:    req.Username,
		Password:    password,
		FromAddress: req.FromAddress,
		FromName:    req.FromName,
		UseTls:      req.UseTLS,
	}); err != nil {
		writeErrWithLog(w, h.logger, "admin smtp: update failed", apierror.Internal(err))
		return
	}
	w.WriteHeader(http.StatusNoContent)
}

func (h *handlers) handleTestSMTPConfig(w http.ResponseWriter, r *http.Request) {
	caller, ok := requireUser(w, r)
	if !ok {
		return
	}
	if caller.Email == nil || *caller.Email == "" {
		writeErr(w, apierror.BadRequest("no_email_on_file",
			"set your email in /settings before testing SMTP"))
		return
	}

	// Use a short-circuit context so the SMTP send doesn't hang the
	// HTTP request indefinitely on a misconfigured host.
	ctx, cancel := context.WithTimeout(r.Context(), 30*time.Second)
	defer cancel()

	sender := mailer.NewSMTPSender(h.pool, h.logger)
	textBody := "This is a test message from your Minstrel server.\n\nIf you got this, your SMTP config is working.\n\n— Minstrel"
	htmlBody := "<p>This is a test message from your Minstrel server.</p><p>If you got this, your SMTP config is working.</p><p>— Minstrel</p>"
	if err := sender.Send(ctx, *caller.Email, "Minstrel SMTP test", textBody, htmlBody); err != nil {
		if errors.Is(err, mailer.ErrNotConfigured) {
			writeErr(w, apierror.BadRequest("not_configured",
				"SMTP is not enabled or required fields are empty"))
			return
		}
		h.logger.Error("admin smtp: test send failed", "err", err)
		writeErr(w, &apierror.Error{Status: http.StatusInternalServerError, Code: "send_failed", Message: err.Error()})
		return
	}
	w.WriteHeader(http.StatusNoContent)
}
