import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/** Model lapisan image: layer ID berantai + cache seperti builder Docker. */
public class LapisanImage {

    /** Satu instruksi Dockerfile beserta "konten" berkas yang dibawanya. */
    record Instruksi(String teks, String konten) {}

    /** ID lapisan = hash(parent + instruksi + konten), seperti cache Docker. */
    static String idLapisan(String idOrtu, Instruksi i) {
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            String bahan = (idOrtu == null ? "-" : idOrtu) + "|" + i.teks() + "|" + i.konten();
            byte[] cerna = md.digest(bahan.getBytes(StandardCharsets.UTF_8));
            StringBuilder hex = new StringBuilder();
            for (int j = 0; j < 6; j++) {
                hex.append(String.format("%02x", cerna[j]));
            }
            return hex.toString();
        } catch (Exception e) {
            throw new IllegalStateException(e);
        }
    }

    /** Jalankan "docker build": HIT bila ID lapisan pernah dibangun sebelumnya. */
    static void build(String judul, List<Instruksi> dockerfile, Map<String, Boolean> cache) {
        System.out.println("== " + judul + " ==");
        String idOrtu = null;
        int hit = 0;
        for (Instruksi i : dockerfile) {
            String id = idLapisan(idOrtu, i);
            boolean tercache = cache.containsKey(id);
            if (tercache) {
                hit++;
            } else {
                cache.put(id, true);
            }
            System.out.printf("  %-44s %s  %s%n", i.teks(), id, tercache ? "HIT" : "MISS");
            idOrtu = id;
        }
        System.out.printf("  lapisan terpakai ulang: %d/%d%n%n", hit, dockerfile.size());
    }

    public static void main(String[] args) {
        List<Instruksi> dockerfile = List.of(
                new Instruksi("FROM eclipse-temurin:25-jre", "image dasar"),
                new Instruksi("COPY libs/util.jar /app/libs/util.jar", "util.jar v1.0.0"),
                new Instruksi("COPY target/toko-app.jar /app/app.jar", "toko-app.jar v1.0.0"),
                new Instruksi("ENTRYPOINT [\"java\",\"-jar\",\"/app/app.jar\"]", "-"));

        Map<String, Boolean> cache = new HashMap<>();
        build("Build 1: cache masih kosong", dockerfile, cache);
        build("Build 2: tanpa perubahan apa pun", dockerfile, cache);

        List<Instruksi> kodeBaru = new ArrayList<>(dockerfile);
        kodeBaru.set(2, new Instruksi("COPY target/toko-app.jar /app/app.jar", "toko-app.jar v1.0.1"));
        build("Build 3: hanya kode aplikasi berubah", kodeBaru, cache);

        // Urutan instruksi memengaruhi jumlah lapisan yang bisa dipakai ulang.
        List<Instruksi> srcDulu = List.of(
                new Instruksi("FROM eclipse-temurin:25-jre", "image dasar"),
                new Instruksi("COPY target/toko-app.jar /app/app.jar", "toko-app.jar v2.0.0"),
                new Instruksi("COPY libs/util.jar /app/libs/util.jar", "util.jar v1.0.0"),
                new Instruksi("ENTRYPOINT [\"java\",\"-jar\",\"/app/app.jar\"]", "-"));
        build("Build 4: kode disalin lebih dulu (urutan kurang tepat)", srcDulu, cache);

        List<Instruksi> depDulu = List.of(
                new Instruksi("FROM eclipse-temurin:25-jre", "image dasar"),
                new Instruksi("COPY libs/util.jar /app/libs/util.jar", "util.jar v1.0.0"),
                new Instruksi("COPY target/toko-app.jar /app/app.jar", "toko-app.jar v2.0.0"),
                new Instruksi("ENTRYPOINT [\"java\",\"-jar\",\"/app/app.jar\"]", "-"));
        build("Build 5: dependency dulu, kode paling akhir (urutan tepat)", depDulu, cache);
    }
}
