import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDate;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * Penyimpanan berkas untuk lapisan repository: satu tugas satu baris dengan
 * format "id|judul|pemilik|status|tenggat" (karakter | karenanya dilarang
 * ValidasiWeb). Berkas dibaca saat konstruksi bila sudah ada; setiap mutasi
 * menulis ulang seluruh isi agar isi berkas selalu konsisten. Id berlanjut
 * dari id tertinggi yang termuat, sehingga restart tidak mengulang id.
 * Ke gagalan I/O dibungkus UncheckedIOException agar kontrak tetap rapi.
 */
public class RepositoryTugasWebBerkas implements RepositoryTugasWeb {

    private final Path berkas;
    private final Map<Integer, TugasWeb> data = new LinkedHashMap<>();
    private int urutan = 0;

    public RepositoryTugasWebBerkas(Path berkas) {
        this.berkas = berkas;
        if (Files.exists(berkas)) {
            muat();
        }
    }

    /** Jumlah tugas yang termuat saat konstruksi; nol bila berkas baru. */
    public int jumlahTermuat() {
        return data.size();
    }

    @Override
    public TugasWeb simpanBaru(TugasWeb tugas) {
        urutan++;
        TugasWeb tersimpan = tugas.denganId(urutan);
        data.put(urutan, tersimpan);
        tulisUlang();
        return tersimpan;
    }

    @Override
    public Optional<TugasWeb> cari(int id) {
        return Optional.ofNullable(data.get(id));
    }

    @Override
    public List<TugasWeb> semua() {
        return new ArrayList<>(data.values());
    }

    @Override
    public Optional<TugasWeb> perbarui(TugasWeb tugas) {
        if (!data.containsKey(tugas.id())) {
            return Optional.empty();
        }
        data.put(tugas.id(), tugas);
        tulisUlang();
        return Optional.of(tugas);
    }

    @Override
    public boolean hapus(int id) {
        boolean ada = data.remove(id) != null;
        if (ada) {
            tulisUlang();
        }
        return ada;
    }

    /** Membaca seluruh baris berkas; baris kosong diabaikan, baris rusak
     *  menghentikan proses dengan nomor baris yang jelas. */
    private void muat() {
        List<String> baris;
        try {
            baris = Files.readAllLines(berkas, StandardCharsets.UTF_8);
        } catch (IOException e) {
            throw new UncheckedIOException("gagal membaca " + berkas.getFileName(), e);
        }
        int nomor = 0;
        for (String isi : baris) {
            nomor++;
            if (isi.isBlank()) {
                continue;
            }
            TugasWeb tugas = parseBaris(nomor, isi);
            data.put(tugas.id(), tugas);
        }
        urutan = data.keySet().stream()
                .mapToInt(Integer::intValue)
                .max().orElse(0);
    }

    /** Mengurai satu baris "id|judul|pemilik|status|tenggat". */
    private static TugasWeb parseBaris(int nomor, String isi) {
        String[] bagian = isi.split("\\|", 5);
        if (bagian.length != 5) {
            throw new IllegalStateException("baris " + nomor + " rusak: \"" + isi + "\"");
        }
        try {
            int id = Integer.parseInt(bagian[0].strip());
            return new TugasWeb(id, bagian[1], bagian[2],
                    StatusTugasWeb.valueOf(bagian[3].strip()),
                    LocalDate.parse(bagian[4].strip()));
        } catch (RuntimeException e) {
            throw new IllegalStateException("baris " + nomor + " rusak: \"" + isi + "\"", e);
        }
    }

    /** Menulis ulang seluruh isi ke berkas, satu tugas satu baris. */
    private void tulisUlang() {
        List<String> baris = new ArrayList<>();
        for (TugasWeb t : data.values()) {
            baris.add(t.id() + "|" + t.judul() + "|" + t.pemilik()
                    + "|" + t.status() + "|" + t.tenggat());
        }
        try {
            Files.write(berkas, baris, StandardCharsets.UTF_8);
        } catch (IOException e) {
            throw new UncheckedIOException("gagal menulis " + berkas.getFileName(), e);
        }
    }
}
