import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

public class HalamanProduk {

    record Produk(int id, String nama, long harga) { }

    // Registri comparator ala Spring Data Sort: nama properti -> Comparator.
    static final Map<String, Comparator<Produk>> URUT = Map.of(
            "id", Comparator.comparingInt(Produk::id),
            "nama", Comparator.comparing(Produk::nama),
            "harga", Comparator.comparingLong(Produk::harga));

    // Padanan PageRequest: hasil binding query ?page&size&sort.
    record Permintaan(int halaman, int ukuran, String properti, boolean menurun) {

        static Permintaan dariQuery(String query) {
            int halaman = 0;
            int ukuran = 5;
            String properti = "id";
            boolean menurun = false;
            for (String pasangan : query.split("&")) {
                String[] kv = pasangan.split("=", 2);
                if (kv.length != 2) {
                    continue;
                }
                switch (kv[0]) {
                    case "page" -> halaman = Integer.parseInt(kv[1]);
                    case "size" -> ukuran = Integer.parseInt(kv[1]);
                    case "sort" -> {
                        String[] bagian = kv[1].split(",");
                        properti = bagian[0];
                        menurun = bagian.length > 1
                                && bagian[1].equalsIgnoreCase("desc");
                    }
                }
            }
            return new Permintaan(halaman, ukuran, properti, menurun);
        }

        // Comparator final: urutan utama + tie-breaker id naik.
        Comparator<Produk> comparator() {
            Comparator<Produk> utama = URUT.get(properti);
            if (menurun) {
                utama = utama.reversed();
            }
            return utama.thenComparing(Produk::id);
        }
    }

    // Padanan Page<T>: isi terpotong + metadata lengkap.
    record Halaman<T>(List<T> isi, int halaman, int ukuran, long totalElemen) {

        long totalHalaman() {
            return (totalElemen + ukuran - 1) / ukuran;
        }

        boolean berikutnya() {
            return halaman + 1 < totalHalaman();
        }

        boolean sebelumnya() {
            return halaman > 0;
        }
    }

    static <T> Halaman<T> ambil(List<T> dataUrut, Permintaan m) {
        // Offset dihitung sebagai long agar page*size tidak meluap int.
        long offset = m.halaman() * (long) m.ukuran();
        int dari = (int) Math.min(offset, dataUrut.size());
        int sampai = (int) Math.min(offset + m.ukuran(), dataUrut.size());
        return new Halaman<>(List.copyOf(dataUrut.subList(dari, sampai)),
                m.halaman(), m.ukuran(), dataUrut.size());
    }

    public static void main(String[] args) {
        List<Produk> semua = new ArrayList<>();
        for (int i = 1; i <= 25; i++) {
            semua.add(new Produk(i, String.format("Produk-%02d", i),
                    10_000 + (i % 7) * 5_000L));
        }
        System.out.println("== 25 produk, harga siklis 10000..40000 (ada nilai kembar) ==");
        System.out.println();

        for (String query : new String[]{
                "page=0&size=5&sort=harga,desc",
                "page=1&size=5&sort=harga,desc",
                "page=4&size=5&sort=harga,desc"}) {
            tampilkan(semua, query);
        }
        System.out.println();

        tampilkan(semua, "page=9&size=5&sort=harga,desc");
        System.out.println();

        System.out.println("== Tanpa tie-breaker id: urutan nilai kembar mengikuti urutan data ==");
        List<Produk> tanpaTie = new ArrayList<>(semua);
        tanpaTie.sort(Comparator.comparingLong(Produk::harga)); // List.sort stabil
        List<Integer> idSenilai = tanpaTie.stream()
                .filter(p -> p.harga() == 40_000)
                .map(Produk::id)
                .toList();
        System.out.println("urutan id untuk harga 40000: " + idSenilai);
        System.out.println("List.sort stabil, namun ORDER BY pada database TIDAK dijamin");
        System.out.println("stabil tanpa kolom pembeda -> selalu tambahkan tie-breaker id");
    }

    static void tampilkan(List<Produk> data, String query) {
        Permintaan m = Permintaan.dariQuery(query);
        List<Produk> dataUrut = new ArrayList<>(data);
        dataUrut.sort(m.comparator());
        Halaman<Produk> h = ambil(dataUrut, m);

        System.out.println("Query : " + query);
        System.out.printf("offset=%d  halaman=%d/%d  isi=%s%n",
                m.halaman() * (long) m.ukuran(), h.halaman() + 1, h.totalHalaman(),
                ringkas(h.isi()));
        System.out.printf("  totalElemen=%d  berikutnya=%s  sebelumnya=%s%n",
                h.totalElemen(), h.berikutnya(), h.sebelumnya());
    }

    static List<String> ringkas(List<Produk> isi) {
        return isi.stream()
                .map(p -> p.nama() + "/" + p.harga())
                .toList();
    }
}
