import com.sun.net.httpserver.HttpServer;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.time.LocalDate;
import java.util.Base64;
import java.util.Locale;

/**
 * Titik masuk inti telanjang Bagian 3: repository berkas + gerbang keamanan.
 * Dua sesi server menunjukkan persistensi lintas restart: sesi pertama
 * membuat data pada berkas tugas-web.txt, sesi kedua memuatnya kembali.
 * Berkas dihapus lebih dulu bila ada agar demo selalu mulai bersih dan
 * keluaran identik di setiap eksekusi.
 */
public class AplikasiWeb {

    private static final Path BERKAS = Path.of("tugas-web.txt");

    public static void main(String[] args) throws Exception {
        Files.deleteIfExists(BERKAS);
        System.out.println("=== AplikasiWeb Bagian 3: persistensi + keamanan ===");
        System.out.println("[Bootstrap] tugas-web.txt disiapkan bersih");

        PenggunaWeb budi = new PenggunaWeb("budi", "sandi-budi", PenggunaWeb.Peran.ANGGOTA);
        PenggunaWeb siti = new PenggunaWeb("siti", "sandi-siti", PenggunaWeb.Peran.ADMIN);
        System.out.println("[Bootstrap] pengguna terdaftar: budi (ANGGOTA), siti (ADMIN)");

        HttpClient klien = HttpClient.newHttpClient();

        System.out.println();
        System.out.println("== Sesi 1: server pertama ==");
        HttpServer server = mulai();
        try {
            String dasar = dasar(server);
            kirim(klien, dasar, null, "GET", "/api/tugas", null);
            kirim(klien, dasar, "Bearer bukan-basic", "GET", "/api/tugas", null);
            kirim(klien, dasar, otorisasi("budi", "salah-total"), "GET", "/api/tugas", null);
            kirim(klien, dasar, otorisasi(budi), "POST", "/api/tugas",
                    "{\"judul\":\"Belajar arsitektur berlapis\",\"pemilik\":\"Budi\","
                            + "\"tenggat\":\"2026-02-01\"}");
            kirim(klien, dasar, otorisasi(budi), "POST", "/api/tugas",
                    "{\"judul\":\"Riset keamanan aplikasi web\",\"pemilik\":\"Dewi\","
                            + "\"tenggat\":\"2026-01-25\"}");
            kirim(klien, dasar, otorisasi(budi), "DELETE", "/api/tugas/1", null);
            kirim(klien, dasar, otorisasi(siti), "DELETE", "/api/tugas/1", null);
            kirim(klien, dasar, otorisasi(budi), "GET", "/api/tugas", null);
        } finally {
            server.stop(0);
        }

        System.out.println();
        System.out.println("== Sesi 2: server baru memuat berkas yang sama ==");
        HttpServer serverBaru = mulai();
        try {
            String dasar = dasar(serverBaru);
            kirim(klien, dasar, otorisasi(siti), "GET", "/api/tugas", null);
            kirim(klien, dasar, otorisasi(budi), "POST", "/api/tugas",
                    "{\"judul\":\"Menulis dokumentasi API\",\"pemilik\":\"Siti\","
                            + "\"tenggat\":\"2026-01-20\"}");
        } finally {
            serverBaru.stop(0);
        }

        System.out.println();
        System.out.println("[Bootstrap] kedua server berhenti; data bertahan di tugas-web.txt");
    }

    /** Server uji: filter keamanan membungkus router di atas repository berkas. */
    private static HttpServer mulai() throws IOException {
        RepositoryTugasWebBerkas repository = new RepositoryTugasWebBerkas(BERKAS);
        System.out.println("[Bootstrap] repository berkas siap: "
                + repository.jumlahTermuat() + " tugas termuat");
        LayananTugasWeb layanan = new LayananTugasWeb(repository, LocalDate.of(2026, 1, 15));
        HttpServer server = HttpServer.create(new InetSocketAddress("127.0.0.1", 0), 0);
        server.createContext("/", new FilterTokenWeb(new RouterWeb(layanan),
                new PenggunaWeb("budi", "sandi-budi", PenggunaWeb.Peran.ANGGOTA),
                new PenggunaWeb("siti", "sandi-siti", PenggunaWeb.Peran.ADMIN)));
        server.start();
        return server;
    }

    /** Alamat dasar server; nomor port tidak dicetak agar keluaran deterministik. */
    private static String dasar(HttpServer server) {
        return "http://127.0.0.1:" + server.getAddress().getPort();
    }

    /** Header Authorization Basic dari pasangan nama dan sandi. */
    private static String otorisasi(String nama, String sandi) {
        byte[] oktet = (nama + ":" + sandi).getBytes(StandardCharsets.UTF_8);
        return "Basic " + Base64.getEncoder().encodeToString(oktet);
    }

    private static String otorisasi(PenggunaWeb pengguna) {
        return otorisasi(pengguna.nama(), pengguna.sandi());
    }

    /** Mengirim satu permintaan lalu mencetak status dan body respons. */
    private static void kirim(HttpClient klien, String dasar, String otorisasi,
            String metode, String path, String body)
            throws IOException, InterruptedException {

        HttpRequest.Builder b = HttpRequest.newBuilder()
                .uri(URI.create(dasar + path))
                .timeout(Duration.ofSeconds(5));
        if (otorisasi != null) {
            b.header("Authorization", otorisasi);
        }
        if (body == null) {
            b.method(metode, HttpRequest.BodyPublishers.noBody());
        } else {
            b.header("Content-Type", "application/json")
                    .method(metode, HttpRequest.BodyPublishers.ofString(
                            body, StandardCharsets.UTF_8));
        }
        HttpResponse<String> r = klien.send(b.build(),
                HttpResponse.BodyHandlers.ofString());
        String www = r.headers().firstValue("WWW-Authenticate")
                .map(w -> " WWW-Authenticate=" + w).orElse("");
        String isi = r.body().isEmpty() ? "(tanpa body)" : r.body();
        System.out.printf(Locale.US, "%-7s %-15s -> %d%s %s%n",
                metode, path, r.statusCode(), www, isi);
    }
}
