import java.util.ArrayDeque;
import java.util.Deque;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/** Workflow GitHub Actions mini: urutan pekerjaan ber-graf needs dan substitusi ekspresi. */
public class AlurKerja {

    /** Satu pekerjaan: nama dan daftar pekerjaan yang harus selesai lebih dulu. */
    record Pekerjaan(String nama, List<String> needs) {}

    /** Konteks ekspresi ${{ ... }} dengan nilai beku ala runner nyata. */
    static final Map<String, String> KONTEKS = Map.of(
            "github.ref", "refs/tags/v2.1.0",
            "github.sha", "9f2ab41c08d7",
            "runner.os", "Linux",
            "env.IMAGE_NAME", "registry.internal/kasirapp");

    /** Urutan eksekusi = urutan topologis graf needs (Kahn). */
    static List<String> urutanEksekusi(List<Pekerjaan> pekerjaan) {
        Map<String, Integer> sisa = new LinkedHashMap<>();
        Map<String, List<String>> pemenuh = new LinkedHashMap<>();
        for (Pekerjaan p : pekerjaan) {
            sisa.put(p.nama(), p.needs().size());
            for (String n : p.needs()) {
                pemenuh.computeIfAbsent(n, k -> new java.util.ArrayList<>()).add(p.nama());
            }
        }
        Deque<String> siap = new ArrayDeque<>();
        for (var e : sisa.entrySet()) {
            if (e.getValue() == 0) {
                siap.add(e.getKey());
            }
        }
        List<String> urutan = new java.util.ArrayList<>();
        while (!siap.isEmpty()) {
            String j = siap.poll();
            urutan.add(j);
            for (String b : pemenuh.getOrDefault(j, List.of())) {
                if (sisa.merge(b, -1, Integer::sum) == 0) {
                    siap.add(b);
                }
            }
        }
        return urutan;
    }

    /** Ganti tiap ${{ path }} dengan nilai dari konteks. */
    static String substitusi(String teks, Map<String, String> konteksTambahan) {
        Map<String, String> semua = new LinkedHashMap<>(KONTEKS);
        semua.putAll(konteksTambahan);
        StringBuilder hasil = new StringBuilder();
        int i = 0;
        while (i < teks.length()) {
            int mulai = teks.indexOf("${{ ", i);
            if (mulai < 0) {
                hasil.append(teks, i, teks.length());
                break;
            }
            int selesai = teks.indexOf(" }}", mulai);
            String path = teks.substring(mulai + 4, selesai);
            hasil.append(teks, i, mulai);
            hasil.append(semua.getOrDefault(path, "<tidak ada: " + path + ">"));
            i = selesai + 3;
        }
        return hasil.toString();
    }

    public static void main(String[] args) {
        System.out.println("== Pemicu ==");
        System.out.println("push ke " + KONTEKS.get("github.ref")
                + " -> workflow release.yml aktif di " + KONTEKS.get("runner.os"));
        System.out.println();

        List<Pekerjaan> pekerjaan = List.of(
                new Pekerjaan("publish", List.of("test-jdk")),
                new Pekerjaan("build", List.of()),
                new Pekerjaan("test-jdk", List.of("build")));

        System.out.println("== Urutan eksekusi (urutan topologis graf needs) ==");
        int nomor = 1;
        for (String j : urutanEksekusi(pekerjaan)) {
            Pekerjaan p = pekerjaan.stream().filter(x -> x.nama().equals(j)).findFirst().orElseThrow();
            String needs = p.needs().isEmpty() ? "-" : String.join(", ", p.needs());
            System.out.printf("  %d. %-9s (needs: %s)%n", nomor++, p.nama(), needs);
        }
        System.out.println();

        System.out.println("== Substitusi ekspresi pada langkah publish ==");
        Map<String, String> turunan = Map.of(
                "env.IMAGE_TAG", KONTEKS.get("github.ref").substring("refs/tags/".length()));
        String[] langkah = {
                "docker build -t ${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }} .",
                "docker push ${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }}"
        };
        for (String l : langkah) {
            System.out.println("  sebelum : " + l);
            System.out.println("  sesudah : " + substitusi(l, turunan));
        }
    }
}
