package main

import "fmt"

// Service mendefinisikan interface yang DIBUTUHKANNYA (consumer side).

type PemberiTahu interface {
	Beritahu(penerima, pesan string) error
}

type LayananPesanan struct {
	pemberiTahu PemberiTahu
}

func NewLayananPesanan(p PemberiTahu) *LayananPesanan {
	return &LayananPesanan{pemberiTahu: p}
}

func (l *LayananPesanan) Konfirmasi(penerima, nomor string) error {
	pesan := fmt.Sprintf("Pesanan %s sudah dikonfirmasi.", nomor)
	return l.pemberiTahu.Beritahu(penerima, pesan)
}

// Implementasi produksi: "email" (simulasi cetak) dengan lifecycle.

type EmailSMTP struct {
	total int
}

func (e *EmailSMTP) Beritahu(penerima, pesan string) error {
	e.total++
	fmt.Printf("[email] ke %s: %s\n", penerima, pesan)
	return nil
}

func (e *EmailSMTP) Close() {
	fmt.Printf("[email] koneksi ditutup, %d pesan terkirim\n", e.total)
}

// Implementasi uji: antrean di memori, tanpa jaringan.

type AntreanMemori struct {
	pesan []string
}

func (a *AntreanMemori) Beritahu(penerima, pesan string) error {
	a.pesan = append(a.pesan, penerima+" | "+pesan)
	return nil
}

// main adalah composition root: satu-satunya tempat perakitan.

func main() {
	fmt.Println("== mode produksi: email ==")
	jalankanProduksi()

	fmt.Println()
	fmt.Println("== mode uji: antrean memori ==")
	antrean := &AntreanMemori{}
	layanan := NewLayananPesanan(antrean)
	if err := layanan.Konfirmasi("budi@example.com", "PSN-103"); err != nil {
		fmt.Println("gagal:", err)
	}
	for _, p := range antrean.pesan {
		fmt.Println("[antrean]", p)
	}
}

func jalankanProduksi() {
	email := &EmailSMTP{}
	defer email.Close() // composition root memegang lifecycle
	layanan := NewLayananPesanan(email)
	if err := layanan.Konfirmasi("budi@example.com", "PSN-101"); err != nil {
		fmt.Println("gagal:", err)
	}
	if err := layanan.Konfirmasi("siti@example.com", "PSN-102"); err != nil {
		fmt.Println("gagal:", err)
	}
}
