package main

import (
	"fmt"
	"strings"
	"sync"
)

// Pesan merekam satu notifikasi yang terkirim.
type Pesan struct {
	Ke     string
	Subjek string
	Isi    string
}

// Notifier adalah abstraksi cara mengirim pesan ke pengguna.
// Implementasi nyata bisa memakai SMTP, webhook, atau antrean pesan.
type Notifier interface {
	Kirim(ke, subjek, isi string) error
}

// EmailNotifier adalah implementasi untuk produksi. Pada contoh ini
// pengiriman disimulasikan ke log memori agar program dapat dijalankan
// tanpa jaringan; versi sungguhan memanggil server SMTP.
type EmailNotifier struct {
	mu       sync.Mutex
	terkirim []Pesan
}

// Kirim mencatat pesan ke log memori seolah terkirim lewat email.
func (e *EmailNotifier) Kirim(ke, subjek, isi string) error {
	if ke == "" {
		return fmt.Errorf("alamat tujuan kosong")
	}
	e.mu.Lock()
	defer e.mu.Unlock()
	e.terkirim = append(e.terkirim, Pesan{Ke: ke, Subjek: subjek, Isi: isi})
	return nil
}

// Terkirim mengembalikan salinan daftar pesan yang sudah tercatat.
func (e *EmailNotifier) Terkirim() []Pesan {
	e.mu.Lock()
	defer e.mu.Unlock()
	return append([]Pesan(nil), e.terkirim...)
}

// PendaftaranService melayani pendaftaran pengguna baru lalu mengirim
// ucapan selamat datang lewat Notifier.
type PendaftaranService struct {
	notifier Notifier
}

// NewPendaftaranService menyuntikkan dependency lewat constructor,
// sehingga pemanggil yang menentukan implementasi Notifier mana yang dipakai.
func NewPendaftaranService(n Notifier) *PendaftaranService {
	return &PendaftaranService{notifier: n}
}

// Daftar mendaftarkan email yang valid, lalu memberi tahu pengguna.
func (s *PendaftaranService) Daftar(email string) error {
	if !strings.Contains(email, "@") || len(email) < 5 {
		return fmt.Errorf("email tidak valid: %q", email)
	}
	return s.notifier.Kirim(email, "Selamat datang", "Terima kasih sudah mendaftar di toko kami.")
}

func main() {
	email := &EmailNotifier{}
	layanan := NewPendaftaranService(email)
	_ = layanan.Daftar("budi@example.com")
	_ = layanan.Daftar("siti@example.com")
	for _, p := range email.Terkirim() {
		fmt.Printf("ke=%s subjek=%q\n", p.Ke, p.Subjek)
	}
}
