import java.util.ArrayList;
import java.util.List;

public class TurunanQuery {

    // Hasil penguraian nama method repository ala Spring Data.
    record QueryTurunan(String operasi, List<String> kriteria, String penyambung,
                        String kolomUrut, boolean menurun) {

        String keSql() {
            StringBuilder sql = new StringBuilder();
            sql.append(switch (operasi) {
                case "find" -> "SELECT * FROM tabel";
                case "count" -> "SELECT COUNT(*) FROM tabel";
                case "exists" -> "SELECT EXISTS(SELECT 1 FROM tabel";
                case "delete" -> "DELETE FROM tabel";
                default -> throw new IllegalStateException();
            });
            if (!kriteria.isEmpty()) {
                sql.append(" WHERE ");
                for (int i = 0; i < kriteria.size(); i++) {
                    sql.append(i == 0 ? "" : " " + penyambung + " ");
                    sql.append(kriteria.get(i)).append(" = ?");
                }
            }
            if (kolomUrut != null) {
                sql.append(" ORDER BY ").append(kolomUrut)
                        .append(menurun ? " DESC" : " ASC");
            }
            if (operasi.equals("exists")) {
                sql.append(")");
            }
            return sql.toString();
        }
    }

    public static void main(String[] args) {
        String[] namaMethod = {
                "findByEmailAndStatus",
                "countByStatus",
                "existsByEmail",
                "deleteByKategori",
                "findByNamaOrderByTanggalDibuatDesc"
        };

        for (String nama : namaMethod) {
            QueryTurunan q = uraikan(nama);
            System.out.println(nama);
            System.out.println("  operasi  : " + q.operasi());
            System.out.println("  kriteria : " + q.kriteria()
                    + (q.kriteria().size() > 1 ? " (disambung " + q.penyambung() + ")" : ""));
            System.out.println("  urutan   : " + (q.kolomUrut() == null
                    ? "-" : q.kolomUrut() + (q.menurun() ? " DESC" : " ASC")));
            System.out.println("  kira-kira: " + q.keSql());
            System.out.println();
        }
    }

    static QueryTurunan uraikan(String nama) {
        String operasi;
        if (nama.startsWith("findBy")) {
            operasi = "find";
        } else if (nama.startsWith("countBy")) {
            operasi = "count";
        } else if (nama.startsWith("existsBy")) {
            operasi = "exists";
        } else if (nama.startsWith("deleteBy")) {
            operasi = "delete";
        } else {
            throw new IllegalArgumentException("Operasi tidak dikenal: " + nama);
        }

        // Buang prefiks operasi + "By".
        String sisa = nama.substring(operasi.length() + 2);

        // Pisahkan klausa OrderBy bila ada.
        String kolomUrut = null;
        boolean menurun = false;
        int idxOrder = sisa.indexOf("OrderBy");
        if (idxOrder >= 0) {
            String urutan = sisa.substring(idxOrder + "OrderBy".length());
            menurun = urutan.endsWith("Desc");
            kolomUrut = menurun
                    ? urutan.substring(0, urutan.length() - "Desc".length())
                    : urutan;
            sisa = sisa.substring(0, idxOrder);
        }

        // Pecah kriteria pada And / Or.
        List<String> kriteria = new ArrayList<>();
        String penyambung = "AND";
        if (sisa.contains("And")) {
            for (String k : sisa.split("And")) {
                kriteria.add(kecilAwal(k));
            }
        } else if (sisa.contains("Or")) {
            penyambung = "OR";
            for (String k : sisa.split("Or")) {
                kriteria.add(kecilAwal(k));
            }
        } else if (!sisa.isEmpty()) {
            kriteria.add(kecilAwal(sisa));
        }
        return new QueryTurunan(operasi, kriteria, penyambung,
                kolomUrut == null ? null : kecilAwal(kolomUrut), menurun);
    }

    // "EmailAndStatus" -> "email": huruf pertama kecil seperti nama property.
    static String kecilAwal(String s) {
        return Character.toLowerCase(s.charAt(0)) + s.substring(1);
    }
}
