package main

import (
	"cmp"
	"fmt"
	"slices"
)

// StrategiDiskon adalah bentuk strategi pertama: interface.
type StrategiDiskon interface {
	Hitung(total int) int
}

// diskonPersen memotong persentase tetap dari total belanja.
type diskonPersen struct{ persen int }

func (d diskonPersen) Hitung(total int) int {
	return total * d.persen / 100
}

// diskonNominal memotong sejumlah rupiah tetap, tidak melebihi total.
type diskonNominal struct{ nominal int }

func (d diskonNominal) Hitung(total int) int {
	return min(d.nominal, total)
}

// Kasir adalah konteks: memegang strategi aktif dan bisa menggantinya
// saat program berjalan lewat SetStrategi.
type Kasir struct {
	strategi StrategiDiskon
}

func (k *Kasir) SetStrategi(s StrategiDiskon) { k.strategi = s }

func (k *Kasir) TotalSetelahDiskon(total int) int {
	return total - k.strategi.Hitung(total)
}

// FungsiDiskon adalah bentuk kedua: strategi sebagai tipe fungsi.
type FungsiDiskon func(total int) int

// persen mengembalikan FungsiDiskon lewat closure.
func persen(p int) FungsiDiskon {
	return func(total int) int { return total * p / 100 }
}

// Barang memperagakan strategi nyata di stdlib: comparator.
type Barang struct {
	Nama  string
	Harga int
}

func main() {
	// Bentuk interface: kasir memilih strategi saat runtime.
	kasir := &Kasir{strategi: diskonPersen{persen: 10}}
	fmt.Println("persen 10%  :", kasir.TotalSetelahDiskon(250000))
	kasir.SetStrategi(diskonNominal{nominal: 30000})
	fmt.Println("nominal     :", kasir.TotalSetelahDiskon(250000))

	// Bentuk fungsi: strategi adalah nilai yang bisa disimpan.
	var f FungsiDiskon = persen(10)
	fmt.Println("fungsi 10%  :", 250000-f(250000))

	// Stdlib: slices.SortFunc menerima comparator sebagai strategi.
	barang := []Barang{
		{"Laptop", 15000000},
		{"Pensil", 3000},
		{"Buku", 45000},
	}
	slices.SortFunc(barang, func(a, b Barang) int {
		return cmp.Compare(a.Harga, b.Harga)
	})
	for _, b := range barang {
		fmt.Printf("%-8s %d\n", b.Nama, b.Harga)
	}
}
