import java.time.LocalDate;
import java.util.List;

/**
 * Lapisan layanan: pemilik aturan bisnis aplikasi web manajemen tugas.
 * Seluruh operasi lewat sini; router tidak pernah menyentuh repository
 * secara langsung, dan repository tidak pernah tahu bahwa HTTP ada.
 * Hari operasional disuntik lewat konstruktor agar aturan "tenggat lampau"
 * bisa diuji dengan tanggal beku, bukan LocalDate.now().
 */
public class LayananTugasWeb {

    /** Batas tugas berstatus TODO per pemilik: aturan bisnis lapisan layanan. */
    public static final int BATAS_TUGAS_AKTIF = 5;

    private final RepositoryTugasWeb repository;
    private final LocalDate hariIni;

    public LayananTugasWeb(RepositoryTugasWeb repository, LocalDate hariIni) {
        this.repository = repository;
        this.hariIni = hariIni;
    }

    /** Hari operasional aplikasi, disuntik lewat konstruktor. */
    public LocalDate hariIni() {
        return hariIni;
    }

    /**
     * Menyusun tugas baru setelah validasi bentuk field dan aturan kontekstual:
     * status selalu TODO, tenggat wajib belum lampau, dan pemilik belum
     * menyentuh batas tugas aktif.
     */
    public TugasWeb buat(PermintaanTugas permintaan) {
        LocalDate tenggat = validasi(permintaan);
        TugasWeb baru = new TugasWeb(0, permintaan.judul(), permintaan.pemilik(),
                StatusTugasWeb.TODO, tenggat);
        return repository.simpanBaru(baru);
    }

    /** Seluruh tugas terurut id. */
    public List<TugasWeb> daftar() {
        return repository.semua();
    }

    /** Satu tugas berdasarkan id; 404 bila tidak ada. */
    public TugasWeb cari(int id) {
        return repository.cari(id)
                .orElseThrow(() -> new TugasTidakDitemukanException(id));
    }

    /**
     * Memperbarui judul, pemilik, dan tenggat; status dipertahankan apa adanya
     * karena transisi status hanya boleh lewat tandaiSelesai.
     */
    public TugasWeb ubah(int id, PermintaanTugas permintaan) {
        TugasWeb tugas = cari(id);
        LocalDate tenggat = validasi(permintaan);
        TugasWeb baru = new TugasWeb(id, permintaan.judul(), permintaan.pemilik(),
                tugas.status(), tenggat);
        return repository.perbarui(baru)
                .orElseThrow(() -> new TugasTidakDitemukanException(id));
    }

    /** Menandai tugas selesai; 409 bila tugas memang sudah SELESAI. */
    public TugasWeb tandaiSelesai(int id) {
        TugasWeb tugas = cari(id);
        if (tugas.status() == StatusTugasWeb.SELESAI) {
            throw new AturanBisnisException("tugas #" + id
                    + " sudah SELESAI, tidak bisa ditandai selesai ulang");
        }
        TugasWeb selesai = tugas.denganStatus(StatusTugasWeb.SELESAI);
        return repository.perbarui(selesai)
                .orElseThrow(() -> new TugasTidakDitemukanException(id));
    }

    /** Menghapus tugas; 404 bila id tidak ada. */
    public void hapus(int id) {
        cari(id);
        repository.hapus(id);
    }

    /**
     * Validasi menyeluruh satu muatan: bentuk field lewat ValidasiWeb
     * (galat dikumpulkan), lalu dua aturan kontekstual — tenggat wajib
     * belum lampau dan pemilik belum menyentuh batas tugas aktif.
     * Mengembalikan tenggat terurai untuk dipakai pemanggil.
     */
    private LocalDate validasi(PermintaanTugas permintaan) {
        List<String> galat = ValidasiWeb.periksa(permintaan);
        if (!galat.isEmpty()) {
            throw new ValidasiGagalException(galat);
        }
        LocalDate tenggat = LocalDate.parse(permintaan.tenggat().strip());
        if (tenggat.isBefore(hariIni)) {
            throw new ValidasiGagalException("tenggat " + tenggat
                    + " sudah lampau, hari operasional " + hariIni);
        }
        long aktif = repository.semua().stream()
                .filter(t -> t.pemilik().equals(permintaan.pemilik().strip()))
                .filter(t -> t.status() == StatusTugasWeb.TODO)
                .count();
        if (aktif >= BATAS_TUGAS_AKTIF) {
            throw new AturanBisnisException("pemilik " + permintaan.pemilik()
                    + " sudah memiliki " + BATAS_TUGAS_AKTIF + " tugas TODO");
        }
        return tenggat;
    }
}
