package main

import (
	"errors"
	"fmt"
	"strings"
)

// ===== Entities: murni, tanpa framework =====

type Pengguna struct {
	Nama  string
	Email string
}

var ErrEmailKosong = errors.New("email wajib diisi")

func (p Pengguna) Valid() error {
	if strings.TrimSpace(p.Email) == "" {
		return ErrEmailKosong
	}
	return nil
}

// ===== Use case: interactor + gerbang (output port) =====

type GerbangPengguna interface {
	Simpan(p Pengguna) error
}

type UseCaseDaftar struct {
	gerbang GerbangPengguna
}

func (u *UseCaseDaftar) Daftar(nama, email string) (Pengguna, error) {
	p := Pengguna{Nama: nama, Email: email}
	if err := p.Valid(); err != nil {
		return Pengguna{}, err
	}
	if err := u.gerbang.Simpan(p); err != nil {
		return Pengguna{}, fmt.Errorf("gagal menyimpan pengguna: %w", err)
	}
	return p, nil
}

// ===== Interface adapter: presenter dan gerbang memori =====

type PresenterKonsol struct{}

func (PresenterKonsol) Tampil(p Pengguna) {
	fmt.Printf("terdaftar: %s <%s>\n", p.Nama, p.Email)
}

type GerbangMemori struct {
	terdaftar []Pengguna
}

func (g *GerbangMemori) Simpan(p Pengguna) error {
	g.terdaftar = append(g.terdaftar, p)
	return nil
}

// ===== Framework & driver: composition root =====

func main() {
	gerbang := &GerbangMemori{}
	useCase := &UseCaseDaftar{gerbang: gerbang}
	presenter := PresenterKonsol{}

	daftar := [][2]string{
		{"Budi", "budi@example.com"},
		{"Siti", ""},
		{"Siti", "siti@example.com"},
	}
	for _, d := range daftar {
		hasil, err := useCase.Daftar(d[0], d[1])
		if err != nil {
			fmt.Printf("gagal: %v\n", err)
			continue
		}
		presenter.Tampil(hasil)
	}
	fmt.Printf("total di gerbang memori: %d\n", len(gerbang.terdaftar))
}
