package main

import (
	"errors"
	"fmt"
)

var ErrSaldoTidakCukup = errors.New("saldo tidak cukup")

// ===== Value object: Uang (immutable) =====

type Uang int64 // dalam Rupiah, tanpa sen

func (u Uang) Tambah(lain Uang) Uang {
	return u + lain // nilai BARU; penerima tidak berubah
}

func (u Uang) Kurang(lain Uang) (Uang, error) {
	if lain > u {
		return 0, fmt.Errorf("%w: butuh %d, tersedia %d", ErrSaldoTidakCukup, lain, u)
	}
	return u - lain, nil
}

// ===== Aggregate root: Rekening =====

type Rekening struct {
	nomor   string
	pemilik string
	saldo   Uang
}

func BukaRekening(nomor, pemilik string, setorAwal Uang) (*Rekening, error) {
	if setorAwal < 0 {
		return nil, errors.New("setoran awal tidak boleh negatif")
	}
	return &Rekening{nomor: nomor, pemilik: pemilik, saldo: setorAwal}, nil
}

func (r *Rekening) Setor(nominal Uang) {
	r.saldo = r.saldo.Tambah(nominal)
}

func (r *Rekening) Tarik(nominal Uang) error {
	sisa, err := r.saldo.Kurang(nominal)
	if err != nil {
		return fmt.Errorf("tarik dari %s: %w", r.nomor, err)
	}
	r.saldo = sisa
	return nil
}

func (r Rekening) Saldo() Uang {
	return r.saldo
}

func (r Rekening) Ringkas() string {
	return fmt.Sprintf("%s (%s): Rp%d", r.nomor, r.pemilik, r.saldo)
}

func main() {
	rek, err := BukaRekening("REK-001", "Budi", 100000)
	if err != nil {
		fmt.Println("gagal:", err)
		return
	}

	rek.Setor(50000)
	fmt.Println(rek.Ringkas())

	if err := rek.Tarik(20000); err != nil {
		fmt.Println("gagal:", err)
	}
	fmt.Println(rek.Ringkas())

	err = rek.Tarik(500000)
	fmt.Println("tarik Rp500.000:", err)
	fmt.Println("saldo tetap:", rek.Ringkas())
	fmt.Println("errors.Is(err, ErrSaldoTidakCukup):", errors.Is(err, ErrSaldoTidakCukup))
}
