import java.io.IOException;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;

/** Harness pengujian berbasis main: PASS/FAIL tanpa pustaka eksternal. */
public class UjiPenyimpananKas {

    private static int lulus = 0;
    private static int gagal = 0;

    public static void main(String[] args) throws IOException {
        ujiBolakBalik();
        ujiBarisCacat();
        ujiNominalNegatif();
        ujiChecksum();
        System.out.println();
        System.out.println("Ringkasan: " + lulus + " lulus, " + gagal + " gagal");
        if (gagal > 0) {
            System.exit(1);   // exit code memberi sinyal ke proses pemanggil
        }
    }

    /** Satu pernyataan uji: catat PASS bila kondisi benar, FAIL bila salah. */
    private static void cek(String nama, boolean kondisi) {
        if (kondisi) {
            lulus++;
            System.out.println("PASS: " + nama);
        } else {
            gagal++;
            System.out.println("FAIL: " + nama);
        }
    }

    /** Simpan lalu muat: data yang kembali harus identik dengan yang disimpan. */
    private static void ujiBolakBalik() throws IOException {
        List<Transaksi> data = List.of(
                new Transaksi(LocalDate.of(2026, 1, 5), "Gaji bulanan Januari",
                        JenisTransaksi.PEMASUKAN, "Gaji", new BigDecimal("8500000.00")),
                new Transaksi(LocalDate.of(2026, 1, 10), "Belanja bahan makanan",
                        JenisTransaksi.PENGELUARAN, "Makanan", new BigDecimal("450000.00")));
        Path berkas = Path.of("uji-bolak-balik.txt");
        PenyimpananKas.simpan(data, berkas);
        PenyimpananKas.HasilMuat hasil = PenyimpananKas.muat(berkas);
        cek("bolak balik memuat 2 transaksi", hasil.transaksi().size() == 2);
        cek("bolak balik identik dengan data asli", hasil.transaksi().equals(data));
        cek("bolak balik tanpa galat", hasil.bersih());
    }

    /** Baris cacat ditolak satu per satu; baris sehat tetap termuat. */
    private static void ujiBarisCacat() throws IOException {
        List<String> baris = new ArrayList<>();
        baris.add(PenyimpananKas.HEADER);
        baris.add("T;2026-01-05;Gaji bulanan Januari;PEMASUKAN;Gaji;8500000.00");
        baris.add("T;2026-01-10;Belanja;PENGELUARAN;Makanan;bukan-angka");
        baris.add("T;2026-01-18;;PENGELUARAN;Makanan;380000.00");
        baris.add(PenyimpananKas.PREFIX_CHECKSUM + PenyimpananKas.checksum(baris));
        Path berkas = Path.of("uji-baris-cacat.txt");
        Files.write(berkas, baris, StandardCharsets.UTF_8);
        PenyimpananKas.HasilMuat hasil = PenyimpananKas.muat(berkas);
        cek("baris cacat memuat 1 transaksi", hasil.transaksi().size() == 1);
        cek("baris cacat melaporkan 2 galat", hasil.galat().size() == 2);
        cek("galat pertama menunjuk baris 3", hasil.galat().get(0).startsWith("Baris 3"));
    }

    /** Nominal negatif lolos parsing angka namun ditolak validasi record. */
    private static void ujiNominalNegatif() throws IOException {
        List<String> baris = new ArrayList<>();
        baris.add(PenyimpananKas.HEADER);
        baris.add("T;2026-01-05;Gaji bulanan Januari;PEMASUKAN;Gaji;8500000.00");
        baris.add("T;2026-01-10;Pengeluaran palsu;PENGELUARAN;Makanan;-500.00");
        baris.add(PenyimpananKas.PREFIX_CHECKSUM + PenyimpananKas.checksum(baris));
        Path berkas = Path.of("uji-nominal-negatif.txt");
        Files.write(berkas, baris, StandardCharsets.UTF_8);
        PenyimpananKas.HasilMuat hasil = PenyimpananKas.muat(berkas);
        cek("nominal negatif ditolak validasi record", hasil.galat().size() == 1
                && hasil.galat().get(0).contains("positif"));
        cek("nominal negatif tidak termuat", hasil.transaksi().size() == 1);
    }

    /** Nominal yang diubah tanpa pembaruan checksum harus menutup seluruh berkas. */
    private static void ujiChecksum() throws IOException {
        List<Transaksi> data = List.of(
                new Transaksi(LocalDate.of(2026, 1, 5), "Gaji bulanan Januari",
                        JenisTransaksi.PEMASUKAN, "Gaji", new BigDecimal("8500000.00")),
                new Transaksi(LocalDate.of(2026, 1, 10), "Belanja bahan makanan",
                        JenisTransaksi.PENGELUARAN, "Makanan", new BigDecimal("450000.00")));
        Path berkas = Path.of("uji-checksum.txt");
        PenyimpananKas.simpan(data, berkas);

        // Mengubah satu nominal tanpa memperbarui checksum di footer.
        List<String> baris = new ArrayList<>(Files.readAllLines(berkas, StandardCharsets.UTF_8));
        baris.set(2, baris.get(2).replace("450000.00", "450000.50"));
        Files.write(berkas, baris, StandardCharsets.UTF_8);

        PenyimpananKas.HasilMuat hasil = PenyimpananKas.muat(berkas);
        cek("checksum gagal menolak seluruh data", hasil.transaksi().isEmpty());
        cek("checksum gagal melaporkan ketidakselarasan",
                !hasil.bersih() && hasil.galat().get(0).startsWith("Checksum"));
    }
}
