package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/fs"
	"os"
	"path/filepath"
)

// Penyimpanan mengurus pembacaan dan penulisan daftar tugas
// ke satu berkas JSON.
type Penyimpanan struct {
	berkas string
}

// PenyimpananBaru membuat penyimpanan yang menunjuk ke path berkas JSON.
func PenyimpananBaru(berkas string) *Penyimpanan {
	return &Penyimpanan{berkas: berkas}
}

// Muat membaca seluruh tugas dari berkas JSON.
// Berkas yang belum ada dianggap daftar kosong untuk aplikasi baru.
func (p *Penyimpanan) Muat() ([]Task, error) {
	isi, err := os.ReadFile(p.berkas)
	if err != nil {
		if errors.Is(err, fs.ErrNotExist) {
			return nil, nil
		}
		return nil, fmt.Errorf("membaca berkas %s: %w", p.berkas, err)
	}

	var tasks []Task
	if err := json.Unmarshal(isi, &tasks); err != nil {
		return nil, fmt.Errorf("mengurai JSON dari %s: %w", p.berkas, err)
	}
	return tasks, nil
}

// Simpan menulis seluruh tugas ke berkas JSON secara atomik:
// data ditulis dulu ke berkas sementara di direktori yang sama,
// lalu berkas sementara menggantikan berkas asli lewat rename.
// Dengan urutan ini, berkas asli tidak pernah berada dalam keadaan
// setengah tertulis.
func (p *Penyimpanan) Simpan(tasks []Task) error {
	if tasks == nil {
		tasks = []Task{}
	}

	data, err := json.MarshalIndent(tasks, "", "  ")
	if err != nil {
		return fmt.Errorf("menyusun JSON: %w", err)
	}
	data = append(data, '\n')

	berkasTmp, err := os.CreateTemp(filepath.Dir(p.berkas), "tugas-*.tmp")
	if err != nil {
		return fmt.Errorf("membuat berkas sementara: %w", err)
	}
	namaTmp := berkasTmp.Name()

	if _, err := berkasTmp.Write(data); err != nil {
		berkasTmp.Close()
		os.Remove(namaTmp)
		return fmt.Errorf("menulis berkas sementara: %w", err)
	}
	if err := berkasTmp.Close(); err != nil {
		os.Remove(namaTmp)
		return fmt.Errorf("menutup berkas sementara: %w", err)
	}
	if err := os.Rename(namaTmp, p.berkas); err != nil {
		os.Remove(namaTmp)
		return fmt.Errorf("mengganti berkas penyimpanan: %w", err)
	}
	return nil
}
