import java.time.LocalDate;
import java.util.List;
import java.util.Locale;

// Layanan gudang: satu-satunya pintu mengubah stok. Seluruh aturan bisnis
// diperiksa di sini sebelum penyimpanan disentuh.
public class Gudang {

    private final GudangRepository repository;

    public Gudang(GudangRepository repository) {
        this.repository = repository;
    }

    /** Mendaftarkan barang baru, selalu dengan stok awal yang diberikan. */
    public Barang daftar(Barang barang) {
        repository.simpan(barang);
        return barang;
    }

    /** Menambah stok dan mencatat mutasi MASUK dalam satu operasi. */
    public MutasiStok barangMasuk(String kode, int jumlah, LocalDate tanggal,
            String petugas, String catatan) {
        Barang barang = cariWajib(kode);
        cekJumlahPositif(jumlah);
        MutasiStok mutasi = repository.catat(new MutasiStok(0, kode,
                JenisMutasi.MASUK, jumlah, tanggal, petugas, catatan));
        repository.perbaruiStok(kode, barang.stok() + jumlah);
        return mutasi;
    }

    /** Mengurangi stok dan mencatat mutasi KELUAR; menolak bila stok kurang. */
    public MutasiStok barangKeluar(String kode, int jumlah, LocalDate tanggal,
            String petugas, String catatan) {
        Barang barang = cariWajib(kode);
        cekJumlahPositif(jumlah);
        if (barang.stok() < jumlah) {
            throw new StokTidakCukupException("stok " + barang.nama() + " tersisa "
                    + barang.stok() + " " + barang.satuan() + ", diminta " + jumlah);
        }
        MutasiStok mutasi = repository.catat(new MutasiStok(0, kode,
                JenisMutasi.KELUAR, jumlah, tanggal, petugas, catatan));
        repository.perbaruiStok(kode, barang.stok() - jumlah);
        return mutasi;
    }

    /** Satu barang berdasarkan kode; melempar bila tidak terdaftar. */
    public Barang cariBarang(String kode) {
        return cariWajib(kode);
    }

    /** Seluruh barang, terurut naik berdasarkan kode. */
    public List<Barang> semuaBarang() {
        return repository.semuaBarang();
    }

    /** Seluruh mutasi dalam urutan pencatatan. */
    public List<MutasiStok> riwayat() {
        return repository.riwayat();
    }

    /** Pencarian kata kunci pada kode dan nama, tidak peka huruf besar-kecil. */
    public List<Barang> cariKataKunci(String kataKunci) {
        String kunci = kataKunci.toLowerCase(Locale.ROOT);
        return repository.semuaBarang().stream()
                .filter(b -> b.nama().toLowerCase(Locale.ROOT).contains(kunci)
                        || b.kode().toLowerCase(Locale.ROOT).contains(kunci))
                .toList();
    }

    /** Barang yang stoknya menyentuh batas minimum atau sudah lewat. */
    public List<Barang> barangPerluRestok() {
        return repository.semuaBarang().stream()
                .filter(Barang::perluRestok)
                .toList();
    }

    /** Seluruh mutasi satu barang (kartu stok), dalam urutan pencatatan. */
    public List<MutasiStok> riwayatBarang(String kode) {
        cariWajib(kode);
        return repository.riwayat().stream()
                .filter(m -> m.kodeBarang().equals(kode))
                .toList();
    }

    private Barang cariWajib(String kode) {
        Barang barang = repository.cari(kode);
        if (barang == null) {
            throw new BarangTidakDitemukanException(
                    "kode barang tidak terdaftar: " + kode);
        }
        return barang;
    }

    private static void cekJumlahPositif(int jumlah) {
        if (jumlah <= 0) {
            throw new GudangException("jumlah mutasi harus positif: " + jumlah);
        }
    }
}
