import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

public class DaftarSiapProduksi {

    // Hasil satu pemeriksaan: lulus atau tidak, dengan keterangan singkat.
    record Hasil(boolean lulus, String keterangan) { }

    // Satu butir daftar periksa: nama + evaluator atas peta konfigurasi.
    record Pemeriksa(String nama, Function<Map<String, String>, Hasil> cek) { }

    static Map<String, String> konfigDev() {
        Map<String, String> cfg = new LinkedHashMap<>();
        cfg.put("server.port", "8080");
        cfg.put("logging.level.root", "DEBUG");
        cfg.put("management.endpoints.web.exposure.include", "*");
        cfg.put("spring.datasource.password", "sandi-dev");
        cfg.put("server.shutdown", "immediate");
        cfg.put("spring.servlet.multipart.max-file-size", "5MB");
        return cfg;
    }

    static Map<String, String> konfigProd() {
        Map<String, String> cfg = new LinkedHashMap<>();
        cfg.put("server.port", "8443");
        cfg.put("logging.level.root", "INFO");
        cfg.put("management.endpoints.web.exposure.include", "health,info");
        cfg.put("spring.datasource.password", "${DB_PASSWORD}");
        cfg.put("server.shutdown", "graceful");
        cfg.put("management.endpoint.health.probes.enabled", "true");
        cfg.put("spring.servlet.multipart.max-file-size", "5MB");
        return cfg;
    }

    static List<Pemeriksa> daftarPeriksa() {
        List<Pemeriksa> butir = new ArrayList<>();

        butir.add(new Pemeriksa("port server dalam rentang valid", cfg -> {
            int port = Integer.parseInt(cfg.getOrDefault("server.port", "8080"));
            return port >= 1 && port <= 65_535
                    ? new Hasil(true, "port=" + port)
                    : new Hasil(false, "port=" + port + " di luar 1-65535");
        }));

        butir.add(new Pemeriksa("log level bukan DEBUG/TRACE", cfg -> {
            String tingkat = cfg.getOrDefault("logging.level.root", "INFO").toUpperCase();
            return tingkat.equals("DEBUG") || tingkat.equals("TRACE")
                    ? new Hasil(false, "root=" + tingkat)
                    : new Hasil(true, "root=" + tingkat);
        }));

        butir.add(new Pemeriksa("eksposur Actuator tidak \"*\"", cfg -> {
            String incl = cfg.getOrDefault(
                    "management.endpoints.web.exposure.include", "health");
            return incl.equals("*")
                    ? new Hasil(false, "include=* (semua endpoint terbuka)")
                    : new Hasil(true, "include=" + incl);
        }));

        butir.add(new Pemeriksa("rahasia ter-eksternalisasi", cfg -> {
            String sandi = cfg.get("spring.datasource.password");
            if (sandi == null) {
                return new Hasil(false, "tidak disetel");
            }
            return sandi.startsWith("${")
                    ? new Hasil(true, "placeholder lingkungan")
                    : new Hasil(false, "nilai tetap tertulis di berkas");
        }));

        butir.add(new Pemeriksa("graceful shutdown aktif", cfg -> {
            String mode = cfg.getOrDefault("server.shutdown", "immediate");
            return mode.equals("graceful")
                    ? new Hasil(true, "shutdown=graceful")
                    : new Hasil(false, "shutdown=" + mode);
        }));

        butir.add(new Pemeriksa("health probe aktif", cfg -> {
            String aktif = cfg.get("management.endpoint.health.probes.enabled");
            return "true".equals(aktif)
                    ? new Hasil(true, "probes=true")
                    : new Hasil(false, "probes tidak disetel");
        }));

        butir.add(new Pemeriksa("batas unggahan berkas disetel", cfg -> {
            String batas = cfg.get("spring.servlet.multipart.max-file-size");
            return batas != null
                    ? new Hasil(true, "max-file-size=" + batas)
                    : new Hasil(false, "max-file-size tidak disetel");
        }));

        return butir;
    }

    public static void main(String[] args) {
        Map<String, String> dev = konfigDev();
        Map<String, String> prod = konfigProd();
        List<Pemeriksa> butir = daftarPeriksa();

        System.out.println("== Konfigurasi yang diperiksa ==");
        tampilKonfigurasi("dev ", dev);
        tampilKonfigurasi("prod", prod);
        System.out.println();

        System.out.println("== Hasil daftar periksa ==");
        int lulusDev = 0;
        int lulusProd = 0;
        int no = 0;
        for (Pemeriksa p : butir) {
            no++;
            Hasil d = p.cek().apply(dev);
            Hasil pr = p.cek().apply(prod);
            lulusDev += d.lulus() ? 1 : 0;
            lulusProd += pr.lulus() ? 1 : 0;
            System.out.printf("%d. %-42s dev %s   prod %s%n",
                    no, p.nama(), ringkas(d), ringkas(pr));
        }

        System.out.println();
        System.out.println("== Rekap ==");
        System.out.printf("dev : %d/%d lulus -> jangan naikkan konfigurasi ini ke produksi%n",
                lulusDev, butir.size());
        System.out.printf("prod: %d/%d lulus -> siap dikemas dan digulirkan%n",
                lulusProd, butir.size());
    }

    static void tampilKonfigurasi(String label, Map<String, String> cfg) {
        StringBuilder baris = new StringBuilder("[" + label + "] ");
        for (Map.Entry<String, String> e : cfg.entrySet()) {
            baris.append(e.getKey()).append('=')
                    .append(e.getValue()).append("  ");
        }
        System.out.println(baris);
    }

    static String ringkas(Hasil h) {
        return (h.lulus() ? "LULUS " : "GAGAL ") + "(" + h.keterangan() + ")";
    }
}
