package recovery import ( "context" "log" "git.k6n.net/mats/go-cart-actor/pkg/cart" ) // LoggingNotifier is the v0 default: it writes one INFO line per candidate // and returns nil. It deliberately does NOT pretend to send anything. // // Future implementations (MailerSend, FCM, APNs) implement the same Notifier // interface; the scanner doesn't change. LoggingNotifier also serves as the // "dry-run" mode in production by leaving the seam unconfigured. type LoggingNotifier struct{} // NewLoggingNotifier returns a Notifier that logs every candidate. The struct // is empty today; the constructor is kept for parity with future notifiers // that need credentials / config. func NewLoggingNotifier() *LoggingNotifier { return &LoggingNotifier{} } // NotifyAbandoned logs the cart id, contact present, and a tiny summary. // Push tokens are summarized by platform only — never log the raw token, so // a misconfigured log aggregator can't leak device handles. func (LoggingNotifier) NotifyAbandoned(_ context.Context, c RecoveryCandidate) error { hasEmail := c.Email != "" hasPush := len(c.PushTokens) > 0 channel := "none" switch { case hasEmail && hasPush: channel = "email+push" case hasEmail: channel = "email" case hasPush: channel = "push" } log.Printf( "recovery [dry-run]: cart=%d user=%q channel=%s items=%d total=%d currency=%s email=%q pushPlatforms=%v lastChangeUnix=%d", c.CartID, c.UserID, channel, c.ItemCount, c.TotalIncVat, c.Currency, c.Email, platformsOf(c.PushTokens), c.LastChangeUnix, ) return nil } func platformsOf(tokens []cart.PushToken) []string { out := make([]string, 0, len(tokens)) for _, t := range tokens { out = append(out, t.Platform) } return out }