import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.Objects;

// Transaksi sirkulasi: berubah saat buku dikembalikan, plus hitungan denda.
public class Peminjaman {

    /** Lama peminjaman standar, dalam hari. */
    public static final int LAMA_PINJAM_HARI = 14;

    private final Buku buku;
    private final Anggota anggota;
    private final LocalDate tanggalPinjam;
    private final LocalDate jatuhTempo;
    private LocalDate tanggalKembali; // null selama buku belum kembali

    public Peminjaman(Buku buku, Anggota anggota, LocalDate tanggalPinjam) {
        this(buku, anggota, tanggalPinjam,
                tanggalPinjam.plusDays(LAMA_PINJAM_HARI), null);
    }

    /** Constructor pemulihan: seluruh field diisi langsung saat memuat berkas. */
    public Peminjaman(Buku buku, Anggota anggota, LocalDate tanggalPinjam,
                      LocalDate jatuhTempo, LocalDate tanggalKembali) {
        this.buku = Objects.requireNonNull(buku, "buku tidak boleh null");
        this.anggota = Objects.requireNonNull(anggota, "anggota tidak boleh null");
        this.tanggalPinjam =
                Objects.requireNonNull(tanggalPinjam, "tanggalPinjam tidak boleh null");
        this.jatuhTempo =
                Objects.requireNonNull(jatuhTempo, "jatuhTempo tidak boleh null");
        if (jatuhTempo.isBefore(tanggalPinjam)) {
            throw new IllegalArgumentException(
                    "jatuhTempo " + jatuhTempo + " lebih awal dari tanggalPinjam " + tanggalPinjam);
        }
        if (tanggalKembali != null && tanggalKembali.isBefore(tanggalPinjam)) {
            throw new IllegalArgumentException(
                    "tanggalKembali " + tanggalKembali + " lebih awal dari tanggalPinjam "
                            + tanggalPinjam);
        }
        this.tanggalKembali = tanggalKembali;
    }

    public Buku buku() {
        return buku;
    }

    public Anggota anggota() {
        return anggota;
    }

    public LocalDate tanggalPinjam() {
        return tanggalPinjam;
    }

    public LocalDate jatuhTempo() {
        return jatuhTempo;
    }

    /** Tanggal kembali aktual; null bila buku masih di tangan anggota. */
    public LocalDate tanggalKembali() {
        return tanggalKembali;
    }

    /** Apakah buku masih dipinjam? */
    public boolean aktif() {
        return tanggalKembali == null;
    }

    /** Catat pengembalian dan kunci transaksi agar tidak dikembalikan dua kali. */
    public void kembalikan(LocalDate tanggal) {
        Objects.requireNonNull(tanggal, "tanggal tidak boleh null");
        if (tanggal.isBefore(tanggalPinjam)) {
            throw new IllegalArgumentException(
                    "tanggal kembali " + tanggal + " lebih awal dari tanggal pinjam "
                            + tanggalPinjam);
        }
        if (!aktif()) {
            throw new IllegalStateException("peminjaman sudah selesai");
        }
        this.tanggalKembali = tanggal;
    }

    /**
     * Selisih hari dari jatuh tempo; 0 bila tidak terlambat.
     * Acuan: tanggal kembali aktual bila transaksi sudah selesai,
     * atau tanggalHitung untuk pinjaman yang masih berjalan.
     */
    public long hariTerlambat(LocalDate tanggalHitung) {
        LocalDate acuan = (tanggalKembali != null) ? tanggalKembali : tanggalHitung;
        return Math.max(0, ChronoUnit.DAYS.between(jatuhTempo, acuan));
    }

    /** Denda = hari terlambat dikali tarif per hari; tanpa pembulatan. */
    public BigDecimal denda(LocalDate tanggalHitung, BigDecimal tarifPerHari) {
        Objects.requireNonNull(tarifPerHari, "tarifPerHari tidak boleh null");
        return BigDecimal.valueOf(hariTerlambat(tanggalHitung)).multiply(tarifPerHari);
    }

    // toString eksplisit: hanya memuat data domain, tanpa alamat objek.
    @Override
    public String toString() {
        return "Peminjaman[judul=" + buku.judul() + ", anggota=" + anggota.nama()
                + ", pinjam=" + tanggalPinjam + ", jatuhTempo=" + jatuhTempo
                + ", kembali=" + (aktif() ? "-" : tanggalKembali) + "]";
    }
}
