package main

import "fmt"

// Stats meniru sql.DBStats: angka untuk memantau kesehatan pool.
type Stats struct {
	MaxOpenConnections int
	OpenConnections    int
	InUse              int
	WaitCount          int64
}

// Pool adalah tiruan *sql.DB: dibuka sekali, dipakai bersama
// seluruh handler, dan ditutup sekali saat aplikasi berhenti.
type Pool struct {
	maxOpen  int
	terpakai int
	menunggu int64
	ditutup  bool
}

// OpenPool membuka pool dengan batas koneksi maksimum.
// sql.Open pada database nyata tidak langsung terhubung;
// koneksi dibuat malas saat query pertama.
func OpenPool(maxOpen int) *Pool {
	return &Pool{maxOpen: maxOpen}
}

// CobaAmbil mengambil satu koneksi. Bila pool penuh, pemanggil
// masuk antrean (WaitCount naik) dan permintaan ini ditolak.
func (p *Pool) CobaAmbil() bool {
	if p.ditutup {
		return false
	}
	if p.terpakai >= p.maxOpen {
		p.menunggu++ // pemanggil menunggu koneksi dikembalikan
		return false
	}
	p.terpakai++
	return true
}

// Kembalikan melepas koneksi agar bisa dipakai pemanggil lain.
func (p *Pool) Kembalikan() {
	if p.terpakai > 0 {
		p.terpakai--
	}
}

// Stats melaporkan kondisi pool saat ini.
func (p *Pool) Stats() Stats {
	return Stats{
		MaxOpenConnections: p.maxOpen,
		OpenConnections:    p.terpakai,
		InUse:              p.terpakai,
		WaitCount:          p.menunggu,
	}
}

// Close menutup pool: semua koneksi dilepas dan pool menolak
// permintaan baru. Pada database nyata, Close menutup semua
// koneksi idle sekaligus koneksi yang sedang dipakai.
func (p *Pool) Close() {
	p.ditutup = true
}

func main() {
	// Inisialisasi sekali di awal, bukan sekali per request.
	pool := OpenPool(2)
	defer pool.Close() // shutdown: tutup saat program selesai

	fmt.Println("ambil pertama :", pool.CobaAmbil())
	fmt.Println("ambil kedua   :", pool.CobaAmbil())

	s := pool.Stats()
	fmt.Printf("stats         : inUse=%d/%d waitCount=%d\n", s.InUse, s.MaxOpenConnections, s.WaitCount)

	// Ambil ketiga: pool penuh, pemanggil menunggu.
	fmt.Println("ambil ketiga  :", pool.CobaAmbil())

	pool.Kembalikan()
	s = pool.Stats()
	fmt.Printf("stats         : inUse=%d/%d waitCount=%d\n", s.InUse, s.MaxOpenConnections, s.WaitCount)

	fmt.Println("ambil ulang   :", pool.CobaAmbil())
	pool.Kembalikan()
	pool.Kembalikan()

	pool.Close()
	fmt.Println("setelah close :", pool.CobaAmbil())
}
