import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Locale;

// Bagian 2: aturan bisnis transaksi masuk dan keluar pada layanan Gudang.
public class DemoGudang {

    public static void main(String[] args) {
        Gudang gudang = new Gudang(new GudangRepositoryMemori());
        isiData(gudang);

        // 1. Barang terdaftar dengan stok nol: stok hanya berubah lewat mutasi.
        System.out.println("terdaftar: " + gudang.cariBarang("BRG-001"));

        // 2. Penerimaan barang menaikkan stok dan tercatat di jurnal.
        System.out.println("masuk   1: " + gudang.barangMasuk("BRG-001", 50,
                LocalDate.of(2026, 1, 5), "Budi", "penerimaan awal"));
        System.out.println("stok     : " + gudang.cariBarang("BRG-001").stok() + " RIM");
        System.out.println("masuk   2: " + gudang.barangMasuk("BRG-001", 30,
                LocalDate.of(2026, 1, 10), "Budi", "pembelian rutin"));
        System.out.println("stok     : " + gudang.cariBarang("BRG-001").stok() + " RIM");

        // 3. Permintaan barang menurunkan stok.
        System.out.println("keluar  1: " + gudang.barangKeluar("BRG-001", 25,
                LocalDate.of(2026, 1, 15), "Siti", "permintaan divisi keuangan"));
        System.out.println("stok     : " + gudang.cariBarang("BRG-001").stok() + " RIM");

        // 4. Keluar melebihi stok: ditolak, stok dan jurnal tidak berubah.
        tolak(() -> gudang.barangKeluar("BRG-001", 100,
                LocalDate.of(2026, 1, 20), "Dewi", "permintaan divisi pemasaran"));
        System.out.println("stok     : " + gudang.cariBarang("BRG-001").stok() + " RIM");

        // 5. Kode yang tidak terdaftar ditolak sebelum penyimpanan disentuh.
        tolak(() -> gudang.barangKeluar("BRG-009", 1,
                LocalDate.of(2026, 1, 21), "Siti", "kode salah ketik"));

        // 6. Jumlah tidak positif ditolak di gerbang layanan.
        tolak(() -> gudang.barangMasuk("BRG-001", -5,
                LocalDate.of(2026, 1, 21), "Budi", "salah ketik"));

        // 7. Kode ganda ditolak lapis penyimpanan (padanan pelanggaran PRIMARY KEY).
        tolak(() -> gudang.daftar(new Barang("BRG-001", "Kertas HVS A4", "RIM",
                new BigDecimal("47500"), 0, 10)));

        // 8. Tabel akhir dan jurnal utuh dalam urutan pencatatan.
        System.out.println();
        System.out.println("== Tabel barang ==");
        for (Barang b : gudang.semuaBarang()) {
            System.out.printf(Locale.US, "%-8s %-22s %-6s %11s %6d%n",
                    b.kode(), b.nama(), b.satuan(), formatRupiah(b.harga()), b.stok());
        }
        System.out.println("== Riwayat mutasi ==");
        for (MutasiStok m : gudang.riwayat()) {
            System.out.printf(Locale.US, "#%d %s %-6s %-8s %3d  %s%n",
                    m.id(), m.tanggal(), m.jenis(), m.kodeBarang(), m.jumlah(),
                    m.petugas());
        }
    }

    /** Menjalankan operasi dan mencetak pesan exception domain yang dilempar. */
    private static void tolak(Runnable operasi) {
        try {
            operasi.run();
            System.out.println("diterima: seharusnya ditolak");
        } catch (GudangException e) {
            System.out.println("ditolak : " + e.getMessage());
        }
    }

    static void isiData(Gudang gudang) {
        gudang.daftar(new Barang("BRG-001", "Kertas HVS A4", "RIM",
                new BigDecimal("47500"), 0, 10));
        gudang.daftar(new Barang("BRG-002", "Tinta Printer Hitam", "BOTOL",
                new BigDecimal("185000"), 0, 4));
        gudang.daftar(new Barang("BRG-003", "Pulpen Gel Hitam 0.5", "BOX",
                new BigDecimal("62000"), 0, 5));
    }

    // Format uang sederhana: 185000 menjadi "Rp185.000".
    static String formatRupiah(BigDecimal nilai) {
        return "Rp" + nilai.setScale(0).toPlainString()
                .replaceAll("\\B(?=(\\d{3})+(?!\\d))", ".");
    }
}
