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

/** Arsitektur MVP (Model-View-Presenter) dengan view pasif berupa interface. */
public class ArsitekturMvp {
    public static void main(String[] args) {
        // Lapisan model: penyimpanan data, tidak mengenal UI
        DaftarSiswa model = new DaftarSiswa();

        // Lapisan view: implementasi konsol, hanya mencatat baris
        TampilanKonsol view = new TampilanKonsol();

        // Lapisan presenter: logika aplikasi, hanya mengenal dua interface di atas
        PresenterSiswa presenter = new PresenterSiswa(model, view);

        presenter.muatUlang();                // daftar awal masih kosong
        presenter.tambah("Budi", 85);
        presenter.tambah("Siti", 92);
        presenter.tambah("Budi", 85);         // nama duplikat, jalur error
        presenter.tambah("Dewi", -5);         // nilai di luar rentang, jalur error
        presenter.muatUlang();

        System.out.println("== keluaran TampilanKonsol ==");
        for (String b : view.baris()) {
            System.out.println(b);
        }

        // View kedua membuktikan presenter tidak terikat satu tampilan
        TampilanJson viewJson = new TampilanJson();
        PresenterSiswa presenterJson = new PresenterSiswa(model, viewJson);
        presenterJson.muatUlang();

        System.out.println("== keluaran TampilanJson ==");
        for (String b : viewJson.baris()) {
            System.out.println(b);
        }
    }
}

// ===== lapisan MODEL =====
class DaftarSiswa {
    private final Map<String, Integer> nilai = new LinkedHashMap<>();

    boolean ada(String nama) {
        return nilai.containsKey(nama);
    }

    void simpan(String nama, int nilaiUjian) {
        nilai.put(nama, nilaiUjian);
    }

    Map<String, Integer> semua() {
        return new LinkedHashMap<>(nilai); // salinan terurut sesuai pendaftaran
    }
}

// ===== lapisan VIEW (kontrak pasif, tanpa logika) =====
interface TampilanSiswa {
    void tampilkanDaftar(Map<String, Integer> data);

    void tampilkanInfo(String pesan);

    void tampilkanError(String pesan);
}

class TampilanKonsol implements TampilanSiswa {
    private final List<String> baris = new ArrayList<>();

    @Override
    public void tampilkanDaftar(Map<String, Integer> data) {
        baris.add("Daftar nilai (" + data.size() + " siswa):");
        data.forEach((nama, nilai) -> baris.add("  " + nama + " -> " + nilai));
    }

    @Override
    public void tampilkanInfo(String pesan) {
        baris.add("[INFO] " + pesan);
    }

    @Override
    public void tampilkanError(String pesan) {
        baris.add("[ERROR] " + pesan);
    }

    List<String> baris() {
        return baris;
    }
}

class TampilanJson implements TampilanSiswa {
    private final List<String> baris = new ArrayList<>();

    @Override
    public void tampilkanDaftar(Map<String, Integer> data) {
        StringBuilder sb = new StringBuilder("{\"siswa\": {");
        boolean awal = true;
        for (Map.Entry<String, Integer> e : data.entrySet()) {
            if (!awal) {
                sb.append(", ");
            }
            sb.append("\"").append(e.getKey()).append("\": ").append(e.getValue());
            awal = false;
        }
        baris.add(sb.append("}}").toString());
    }

    @Override
    public void tampilkanInfo(String pesan) {
        baris.add("{\"info\": \"" + pesan + "\"}");
    }

    @Override
    public void tampilkanError(String pesan) {
        baris.add("{\"error\": \"" + pesan + "\"}");
    }

    List<String> baris() {
        return baris;
    }
}

// ===== lapisan PRESENTER =====
class PresenterSiswa {
    private final DaftarSiswa model;
    private final TampilanSiswa view;

    PresenterSiswa(DaftarSiswa model, TampilanSiswa view) {
        this.model = model;
        this.view = view;
    }

    void muatUlang() {
        view.tampilkanDaftar(model.semua());
    }

    void tambah(String nama, int nilaiUjian) {
        if (nama == null || nama.isBlank()) {
            view.tampilkanError("nama tidak boleh kosong");
            return;
        }
        if (nilaiUjian < 0 || nilaiUjian > 100) {
            view.tampilkanError("nilai harus 0-100, diterima: " + nilaiUjian);
            return;
        }
        if (model.ada(nama)) {
            view.tampilkanError("siswa " + nama + " sudah terdaftar");
            return;
        }
        model.simpan(nama, nilaiUjian);
        view.tampilkanInfo("siswa " + nama + " tersimpan");
    }
}
