// Package notify 内的 notifier_email.go 实现 EmailNotifier：通过 SMTP（net/smtp）
// 把通知投递为邮件。它实现 Notifier 接口，由 Dispatcher 在配置启用时注册。
//
// 投递前置条件（任一不满足则静默跳过，返回 nil——per-channel 失败/跳过对
// Dispatcher 非致命）：
//   - 全局 cfg.Enabled 为 true；
//   - 用户 notification_prefs.email_enabled 为 true；
//   - 消息 severity 达到用户 min_severity；
//   - 能解析出该用户的收件邮箱。
//
// SMTP 调用经 transport 抽象注入，测试用 fake transport 断言收件人/正文形态，
// 绝不真的发邮件；生产用 smtpTransport（net/smtp.SendMail）。
package notify

import (
	"context"
	"fmt"
	"log/slog"
	"net/smtp"
	"strings"

	"github.com/google/uuid"
)

// EmailConfig 是 EmailNotifier 的配置，对应 cfg.Notify.Email。
type EmailConfig struct {
	Enabled  bool
	SMTPHost string
	Port     int
	From     string
	Username string
	Password string
}

// addr 返回 "host:port" 形式的 SMTP 地址。
func (c EmailConfig) addr() string {
	return fmt.Sprintf("%s:%d", c.SMTPHost, c.Port)
}

// EmailLookup 把 userID 解析为收件邮箱。由 user.Service 适配注入，避免 notify
// 反向依赖 user 包造成的循环导入。
type EmailLookup interface {
	EmailForUser(ctx context.Context, userID uuid.UUID) (string, error)
}

// SMTPTransport 抽象一次 SMTP 发送，便于测试注入 fake。参数与 net/smtp.SendMail
// 对齐：addr 形如 "host:port"，auth 可为 nil（无认证），from 为发件人，to 为收件人
// 列表，msg 为完整 RFC 822 报文（含头）。
type SMTPTransport interface {
	Send(addr string, auth smtp.Auth, from string, to []string, msg []byte) error
}

// smtpTransport 是 SMTPTransport 的生产实现，直接委托 net/smtp.SendMail。
type smtpTransport struct{}

func (smtpTransport) Send(addr string, auth smtp.Auth, from string, to []string, msg []byte) error {
	return smtp.SendMail(addr, auth, from, to, msg)
}

// EmailNotifier 通过 SMTP 投递邮件，实现 Notifier。
type EmailNotifier struct {
	cfg    EmailConfig
	prefs  PrefsRepository
	lookup EmailLookup
	tr     SMTPTransport
	log    *slog.Logger
}

// NewEmailNotifier 构造 EmailNotifier。tr 为 nil 时用生产 net/smtp 实现；
// log 为 nil 时回落到 slog.Default。
func NewEmailNotifier(cfg EmailConfig, prefs PrefsRepository, lookup EmailLookup, tr SMTPTransport, log *slog.Logger) *EmailNotifier {
	if tr == nil {
		tr = smtpTransport{}
	}
	if log == nil {
		log = slog.Default()
	}
	return &EmailNotifier{cfg: cfg, prefs: prefs, lookup: lookup, tr: tr, log: log}
}

// Name 返回 ChannelEmail。
func (n *EmailNotifier) Name() Channel { return ChannelEmail }

// Send 在满足全部前置条件时发送一封邮件；任一条件不满足静默返回 nil。
// SMTP 调用失败返回错误（Dispatcher 仅记日志，不影响其它通道）。
func (n *EmailNotifier) Send(ctx context.Context, msg Message) error {
	if !n.cfg.Enabled {
		return nil
	}
	p, err := n.prefs.Get(ctx, msg.UserID)
	if err != nil {
		return err
	}
	if !p.EmailEnabled {
		return nil
	}
	if !meetsMinSeverity(msg.Severity, p.MinSeverity) {
		return nil
	}
	to, err := n.lookup.EmailForUser(ctx, msg.UserID)
	if err != nil {
		return err
	}
	if strings.TrimSpace(to) == "" {
		return nil
	}

	var auth smtp.Auth
	if n.cfg.Username != "" {
		auth = smtp.PlainAuth("", n.cfg.Username, n.cfg.Password, n.cfg.SMTPHost)
	}
	body := buildEmailMessage(n.cfg.From, to, msg)
	if err := n.tr.Send(n.cfg.addr(), auth, n.cfg.From, []string{to}, body); err != nil {
		return fmt.Errorf("smtp send: %w", err)
	}
	return nil
}

// buildEmailMessage 组装一封最小 RFC 822 纯文本邮件报文（含 From/To/Subject 头）。
// 主题前缀用 severity 便于客户端过滤；正文带标题、正文与可选链接。
func buildEmailMessage(from, to string, msg Message) []byte {
	subject := msg.Title
	if subject == "" {
		subject = msg.Topic
	}
	var b strings.Builder
	fmt.Fprintf(&b, "From: %s\r\n", from)
	fmt.Fprintf(&b, "To: %s\r\n", to)
	fmt.Fprintf(&b, "Subject: [%s] %s\r\n", strings.ToUpper(string(msg.Severity)), subject)
	b.WriteString("MIME-Version: 1.0\r\n")
	b.WriteString("Content-Type: text/plain; charset=\"UTF-8\"\r\n")
	b.WriteString("\r\n")
	if msg.Body != "" {
		b.WriteString(msg.Body)
		b.WriteString("\r\n")
	}
	if msg.Link != "" {
		fmt.Fprintf(&b, "\r\n%s\r\n", msg.Link)
	}
	return []byte(b.String())
}
