package main

import "fmt"

// Pesanan adalah data yang mengalir melalui pipeline.
type Pesanan struct {
	ID        string
	Pelanggan string
	Token     string
	Total     int
	Ditolak   string
}

// Handler adalah satu tahap pemrosesan: menerima Pesanan, mengembalikan Pesanan.
type Handler func(Pesanan) Pesanan

// Middleware generik: membungkus Handler menjadi Handler baru.
// Bentuknya sama dengan middleware HTTP, hanya tipenya diganti.
type Middleware func(Handler) Handler

// catat mencatat pesanan saat masuk dan hasilnya saat keluar.
func catat(next Handler) Handler {
	return func(p Pesanan) Pesanan {
		fmt.Printf("[log]    masuk  : %s (total=%d)\n", p.ID, p.Total)
		hasil := next(p)
		fmt.Printf("[log]    keluar : %s (total=%d)\n", hasil.ID, hasil.Total)
		return hasil
	}
}

// autentikasi memeriksa token sebelum pesanan diteruskan.
// Token salah: next tidak dipanggil, pipeline berhenti di lapisan ini.
func autentikasi(next Handler) Handler {
	return func(p Pesanan) Pesanan {
		if p.Token != "token-budi" {
			fmt.Printf("[auth]   tolak  : %s (token tidak valid)\n", p.ID)
			p.Ditolak = "token tidak valid"
			return p
		}
		fmt.Printf("[auth]   lolos  : %s\n", p.ID)
		return next(p)
	}
}

// diskonMember memotong total untuk pembelanjaan minimal 100000.
func diskonMember(persen int) Middleware {
	return func(next Handler) Handler {
		return func(p Pesanan) Pesanan {
			if p.Total >= 100000 {
				p.Total -= p.Total * persen / 100
				fmt.Printf("[diskon] potong : %s (%d persen)\n", p.ID, persen)
			}
			return next(p)
		}
	}
}

// proses adalah handler inti yang dibungkus middleware.
func proses(p Pesanan) Pesanan {
	fmt.Printf("[core]   proses: %s untuk %s\n", p.ID, p.Pelanggan)
	return p
}

// chain melipat daftar middleware menjadi satu:
// chain(a, b, c)(h) menghasilkan a(b(c(h))).
func chain(mw ...Middleware) Middleware {
	return func(final Handler) Handler {
		for i := len(mw) - 1; i >= 0; i-- {
			final = mw[i](final)
		}
		return final
	}
}

func main() {
	pipeline := chain(catat, autentikasi, diskonMember(10))(proses)

	pesanan := []Pesanan{
		{ID: "P-001", Pelanggan: "Budi", Token: "token-budi", Total: 250000},
		{ID: "P-002", Pelanggan: "anonim", Token: "salah", Total: 50000},
	}
	for i, p := range pesanan {
		if i > 0 {
			fmt.Println()
		}
		hasil := pipeline(p)
		if hasil.Ditolak != "" {
			fmt.Printf("=> %s gagal : %s\n", hasil.ID, hasil.Ditolak)
		} else {
			fmt.Printf("=> %s selesai, total=%d\n", hasil.ID, hasil.Total)
		}
	}
}
