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

public class PetaHandlerError {

    // Hierarki exception domain ala aplikasi nyata.
    static class KesalahanApi extends RuntimeException {
        KesalahanApi(String pesan) { super(pesan); }
    }

    static class TidakDitemukanException extends KesalahanApi {
        TidakDitemukanException(String pesan) { super(pesan); }
    }

    static class ValidasiGagalException extends KesalahanApi {
        ValidasiGagalException(String pesan) { super(pesan); }
    }

    // RFC 7807 ProblemDetail: format kesalahan baku Spring Boot 3.
    record ProblemDetail(int status, String title, String detail) {
        String json() {
            return "{\"type\":\"about:blank\",\"title\":\"" + title
                    + "\",\"status\":" + status
                    + ",\"detail\":\"" + detail + "\"}";
        }
    }

    public static void main(String[] args) {
        // @RestControllerAdvice mini: exception -> handler.
        Map<Class<? extends Throwable>, Function<Throwable, ProblemDetail>> advice =
                new LinkedHashMap<>();
        advice.put(TidakDitemukanException.class,
                e -> new ProblemDetail(404, "Not Found", e.getMessage()));
        advice.put(ValidasiGagalException.class,
                e -> new ProblemDetail(400, "Bad Request", e.getMessage()));
        advice.put(RuntimeException.class,
                e -> new ProblemDetail(500, "Internal Server Error", "kesalahan tak terduga"));

        layani(advice, "GET /api/produk/99", () -> {
            throw new TidakDitemukanException("Produk 99 tidak ada");
        });
        layani(advice, "POST /api/produk", () -> {
            throw new ValidasiGagalException("Kolom nama tidak boleh kosong");
        });
        layani(advice, "GET /api/laporan", () -> {
            throw new IllegalStateException("sambungan basis data terputus");
        });
    }

    static void layani(Map<Class<? extends Throwable>, Function<Throwable, ProblemDetail>> advice,
                       String permintaan, Runnable handler) {
        System.out.println("Request: " + permintaan);
        try {
            handler.run();
        } catch (Throwable e) {
            ProblemDetail pd = cariHandlerTerdekat(advice, e).apply(e);
            System.out.println("  -> " + pd.status() + " " + pd.title());
            System.out.println("  -> " + pd.json());
            System.out.println();
        }
    }

    // Pencarian handler: naik dari kelas exception konkret ke induknya,
    // persis resolusi @ExceptionHandler pada @ControllerAdvice.
    static Function<Throwable, ProblemDetail> cariHandlerTerdekat(
            Map<Class<? extends Throwable>, Function<Throwable, ProblemDetail>> advice,
            Throwable e) {
        Class<?> kelas = e.getClass();
        while (kelas != null) {
            Function<Throwable, ProblemDetail> handler = advice.get(kelas);
            if (handler != null) {
                return handler;
            }
            kelas = kelas.getSuperclass();
        }
        return x -> new ProblemDetail(500, "Internal Server Error", x.getMessage());
    }
}
