// Package email sends QSO recordings to correspondents via SMTP. Pure Go (no
// CGO) using go-mail; supports implicit SSL (465), STARTTLS (587) or none.
package email

import (
	"fmt"
	"time"

	"github.com/wneessen/go-mail"
)

// Config is the user's SMTP configuration.
type Config struct {
	Host       string
	Port       int
	User       string
	Password   string
	From       string
	ReplyTo    string // optional Reply-To: where replies should go (e.g. a personal inbox)
	Encryption string // "ssl" | "starttls" | "none"
	Auth       bool   // SMTP requires authorization (send username/password)
}

func (c Config) opts() []mail.Option {
	o := []mail.Option{mail.WithPort(c.Port), mail.WithTimeout(30 * time.Second)}
	if c.Auth && c.User != "" {
		// AutoDiscover negotiates whatever mechanism the server advertises
		// (LOGIN, PLAIN, CRAM-MD5, …). OVH, for instance, rejects forced PLAIN.
		o = append(o, mail.WithSMTPAuth(mail.SMTPAuthAutoDiscover), mail.WithUsername(c.User), mail.WithPassword(c.Password))
	}
	switch c.Encryption {
	case "ssl":
		o = append(o, mail.WithSSL())
	case "none":
		o = append(o, mail.WithTLSPolicy(mail.NoTLS))
	default: // starttls
		o = append(o, mail.WithTLSPolicy(mail.TLSMandatory))
	}
	return o
}

// Send delivers a plain-text email to `to`, optionally attaching a file.
func Send(cfg Config, to, subject, body, attachPath string) error {
	if cfg.Host == "" {
		return fmt.Errorf("SMTP server not configured")
	}
	if to == "" {
		return fmt.Errorf("no recipient e-mail")
	}
	from := cfg.From
	if from == "" {
		from = cfg.User
	}
	m := mail.NewMsg()
	if err := m.From(from); err != nil {
		return fmt.Errorf("bad sender %q: %w", from, err)
	}
	if err := m.To(to); err != nil {
		return fmt.Errorf("bad recipient %q: %w", to, err)
	}
	if cfg.ReplyTo != "" {
		if err := m.ReplyTo(cfg.ReplyTo); err != nil {
			return fmt.Errorf("bad reply-to %q: %w", cfg.ReplyTo, err)
		}
	}
	m.Subject(subject)
	m.SetBodyString(mail.TypeTextPlain, body)
	if attachPath != "" {
		m.AttachFile(attachPath)
	}
	client, err := mail.NewClient(cfg.Host, cfg.opts()...)
	if err != nil {
		return fmt.Errorf("smtp client: %w", err)
	}
	if err := client.DialAndSend(m); err != nil {
		return fmt.Errorf("send: %w", err)
	}
	return nil
}
