import java.util.List;

/**
 * Pengukur performa deterministik atas hasil pipeline nyata.
 * Item per tahap dibaca dari statistik HasilPipeline (angka hasil eksekusi),
 * biaya per item dari model tetap TahapPipeline — tidak ada jam dinding yang
 * diukur, sehingga seluruh angka stabil antar eksekusi dan siap diuji.
 */
public final class ProfilPipeline {

    /** Biaya penjadwalan simulasi setiap task yang dikirim ke executor. */
    public static final long BIAYA_PENJADWALAN = 2;

    private ProfilPipeline() {
        // Kelas utilitas: seluruh anggota static.
    }

    /** Menyusun profil per tahap dari statistik hasil pipeline nyata. */
    public static List<BarisProfil> profil(HasilPipeline hasil) {
        long barisLaporan = hasil.laporan().size() + hasil.laporanGalat().size();
        return List.of(
                new BarisProfil(TahapPipeline.BACA, hasil.jumlahBaris()),
                new BarisProfil(TahapPipeline.VALIDASI, hasil.jumlahBaris()),
                new BarisProfil(TahapPipeline.TRANSFORMASI, hasil.jumlahPercobaan()),
                new BarisProfil(TahapPipeline.AGREGASI, hasil.jumlahSah()),
                new BarisProfil(TahapPipeline.TULIS, barisLaporan));
    }

    /** Total biaya seluruh tahap bila dikerjakan sekuensial. */
    public static long totalBiaya(List<BarisProfil> profil) {
        return profil.stream().mapToLong(BarisProfil::total).sum();
    }

    /** Tahap dengan total biaya terbesar — kandidat kemacetan utama. */
    public static BarisProfil kemacetan(List<BarisProfil> profil) {
        BarisProfil terbesar = profil.get(0);
        for (BarisProfil b : profil) {
            if (b.total() > terbesar.total()) {
                terbesar = b;
            }
        }
        return terbesar;
    }

    /** Total biaya tahap yang berjalan paralel per baris (validasi + transformasi). */
    public static long biayaTahapParalel(List<BarisProfil> profil) {
        return profil.stream()
                .filter(b -> b.tahap() == TahapPipeline.VALIDASI
                        || b.tahap() == TahapPipeline.TRANSFORMASI)
                .mapToLong(BarisProfil::total)
                .sum();
    }

    /** Total biaya tahap yang berjalan sekuensial di main (baca + agregasi + tulis). */
    public static long biayaTahapSekuensial(List<BarisProfil> profil) {
        return totalBiaya(profil) - biayaTahapParalel(profil);
    }

    /**
     * Simulasi waktu dinding bila tahap paralel dikerjakan W pekerja:
     * kerja paralel dibagi rata ke atas (ceil), tahap sekuensial tetap utuh.
     * Lebar 1 berarti sekuensial: hasil sama dengan totalBiaya.
     */
    public static long waktuDinding(List<BarisProfil> profil, int lebarParalel) {
        long gelombang = (biayaTahapParalel(profil) + lebarParalel - 1) / lebarParalel;
        return biayaTahapSekuensial(profil) + gelombang;
    }

    /** Throughput model: baris selesai per unit biaya simulasi. */
    public static double throughput(int jumlahBaris, long waktuUnit) {
        return (double) jumlahBaris / waktuUnit;
    }

    /** Biaya penjadwalan bila tiap baris dikirim sebagai satu task. */
    public static long biayaPenjadwalanPerBaris(int jumlahBaris) {
        return jumlahBaris * BIAYA_PENJADWALAN;
    }

    /** Biaya penjadwalan bila baris dikelompokkan menjadi batch berukuran tetap. */
    public static long biayaPenjadwalanPerBatch(int jumlahBaris, int ukuranBatch) {
        int jumlahTask = (jumlahBaris + ukuranBatch - 1) / ukuranBatch;
        return jumlahTask * BIAYA_PENJADWALAN;
    }
}
