package main

import (
	"errors"
	"fmt"
	"maps"
	"slices"
	"sync"
)

// ErrNotFound adalah error domain: item dengan ID tersebut tidak ada.
// Handler HTTP menerjemahkannya menjadi status 404 tanpa mengenali database.
var ErrNotFound = errors.New("item tidak ditemukan")

// Item adalah struct domain murni: tidak mengimport database apa pun.
type Item struct {
	ID    int
	Nama  string
	Harga int
}

// ItemRepository adalah kontrak akses data item.
// Interface dibuat kecil: lima method cukup untuk kebutuhan saat ini.
type ItemRepository interface {
	List() ([]Item, error)
	Get(id int) (Item, error)
	Create(nama string, harga int) (Item, error)
	Update(item Item) error
	Delete(id int) error
}

// MemoryRepository menyimpan item di map. Implementasi ini dipakai
// untuk demo dan test cepat tanpa database aktif.
type MemoryRepository struct {
	mu          sync.Mutex
	berikut     int
	penyimpanan map[int]Item
}

func NewMemoryRepository() *MemoryRepository {
	return &MemoryRepository{berikut: 1, penyimpanan: map[int]Item{}}
}

func (r *MemoryRepository) List() ([]Item, error) {
	r.mu.Lock()
	defer r.mu.Unlock()
	hasil := make([]Item, 0, len(r.penyimpanan))
	for _, id := range slices.Sorted(maps.Keys(r.penyimpanan)) {
		hasil = append(hasil, r.penyimpanan[id])
	}
	return hasil, nil
}

func (r *MemoryRepository) Get(id int) (Item, error) {
	r.mu.Lock()
	defer r.mu.Unlock()
	item, ada := r.penyimpanan[id]
	if !ada {
		return Item{}, ErrNotFound
	}
	return item, nil
}

func (r *MemoryRepository) Create(nama string, harga int) (Item, error) {
	r.mu.Lock()
	defer r.mu.Unlock()
	item := Item{ID: r.berikut, Nama: nama, Harga: harga}
	r.penyimpanan[r.berikut] = item
	r.berikut++
	return item, nil
}

func (r *MemoryRepository) Update(item Item) error {
	r.mu.Lock()
	defer r.mu.Unlock()
	if _, ada := r.penyimpanan[item.ID]; !ada {
		return ErrNotFound
	}
	r.penyimpanan[item.ID] = item
	return nil
}

func (r *MemoryRepository) Delete(id int) error {
	r.mu.Lock()
	defer r.mu.Unlock()
	if _, ada := r.penyimpanan[id]; !ada {
		return ErrNotFound
	}
	delete(r.penyimpanan, id)
	return nil
}

func main() {
	var repo ItemRepository = NewMemoryRepository()

	repo.Create("Kopi Gayo", 45000)
	repo.Create("Teh Melati", 25000)

	item, _ := repo.Get(1)
	fmt.Printf("get(1)   : %+v\n", item)

	item.Harga = 50000
	if err := repo.Update(item); err != nil {
		fmt.Println("update   :", err)
		return
	}

	daftar, _ := repo.List()
	for _, it := range daftar {
		fmt.Printf("list     : %s Rp%d\n", it.Nama, it.Harga)
	}

	if err := repo.Delete(2); err != nil {
		fmt.Println("delete(2):", err)
		return
	}
	if _, err := repo.Get(2); err != nil {
		fmt.Println("get(2)   :", err)
	}
}
