import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;

public class BahasaDomain {
    public static void main(String[] args) {
        LayananEkspedisi ekspedisi = new LayananEkspedisi();

        Pengiriman pertama = ekspedisi.buatPengiriman("Budi", "Siti", 1_500);
        Pengiriman kedua = ekspedisi.buatPengiriman("Dewi", "Budi", 500);

        System.out.println(pertama.ringkas());
        System.out.println(kedua.ringkas());
        System.out.println("Lacak EXP-001: "
                + ekspedisi.lacak("EXP-001").map(Pengiriman::ringkas).orElse("tidak ada"));
        System.out.println("Lacak EXP-003: "
                + ekspedisi.lacak("EXP-003").map(Pengiriman::ringkas).orElse("tidak ada"));
    }
}

// Value object: berat dalam gram, tidak dapat diubah
record Berat(int gram) {
    Berat {
        if (gram <= 0) {
            throw new IllegalArgumentException("berat harus positif: " + gram);
        }
    }
}

// Value object: ongkir dalam rupiah
record Ongkir(long rupiah) {
    Ongkir tambah(Ongkir lain) {
        return new Ongkir(rupiah + lain.rupiah);
    }
}

// Entity: identitasnya nomor resi, bukan isi field
class Pengiriman {
    private final String nomorResi;
    private final String pengirim;
    private final String penerima;
    private final Berat berat;
    private final Ongkir ongkir;

    Pengiriman(String nomorResi, String pengirim, String penerima,
               Berat berat, Ongkir ongkir) {
        this.nomorResi = nomorResi;
        this.pengirim = pengirim;
        this.penerima = penerima;
        this.berat = berat;
        this.ongkir = ongkir;
    }

    String nomorResi() {
        return nomorResi;
    }

    String ringkas() {
        return nomorResi + ": " + pengirim + " -> " + penerima
                + ", " + berat.gram() + " gram, Rp" + ongkir.rupiah();
    }
}

// Domain service: aturan yang melibatkan beberapa objek domain
class LayananEkspedisi {
    private final Map<String, Pengiriman> resi = new LinkedHashMap<>();
    private int urutan = 0;

    Pengiriman buatPengiriman(String pengirim, String penerima, int beratGram) {
        urutan++;
        Berat berat = new Berat(beratGram);
        Ongkir ongkir = hitungOngkir(berat);
        String nomor = String.format("EXP-%03d", urutan);
        Pengiriman pengiriman = new Pengiriman(nomor, pengirim, penerima, berat, ongkir);
        resi.put(nomor, pengiriman);
        return pengiriman;
    }

    Optional<Pengiriman> lacak(String nomorResi) {
        return Optional.ofNullable(resi.get(nomorResi));
    }

    private Ongkir hitungOngkir(Berat berat) {
        long tarifPerSeratusGram = 2_000;
        int blokSeratusGram = (berat.gram() + 99) / 100;
        return new Ongkir(blokSeratusGram * tarifPerSeratusGram);
    }
}
