package main

import (
	"fmt"
	"os"
	"path/filepath"
)

func main() {
	// Direktori sementara dengan nama acak untuk semua berkas kerja.
	dir, err := os.MkdirTemp("", "contoh-temp-*")
	if err != nil {
		fmt.Println("gagal membuat direktori:", err)
		return
	}
	defer os.RemoveAll(dir)

	// CreateTemp mengganti tanda * dengan string acak agar nama unik.
	tmp, err := os.CreateTemp(dir, "catatan-*.txt")
	if err != nil {
		fmt.Println("gagal membuat berkas sementara:", err)
		return
	}
	defer os.Remove(tmp.Name()) // cadangan; RemoveAll sudah menyapu bersih

	isi := "laporan perantara Budi"
	if _, err := tmp.WriteString(isi); err != nil {
		fmt.Println("gagal menulis:", err)
		tmp.Close()
		return
	}
	if err := tmp.Close(); err != nil {
		fmt.Println("gagal menutup berkas:", err)
		return
	}

	data, err := os.ReadFile(tmp.Name())
	if err != nil {
		fmt.Println("gagal membaca berkas sementara:", err)
		return
	}
	fmt.Println("berkas sementara : dibuat")
	fmt.Printf("isi sementara    : %s (%d byte)\n", data, len(data))

	// Pola tulis atomik: tulis ke berkas sementara, lalu rename ke nama final.
	akhir := filepath.Join(dir, "data.txt")
	if err := tulisAtomik(akhir, []byte("data final Siti")); err != nil {
		fmt.Println("gagal menulis atomik:", err)
		return
	}
	data, err = os.ReadFile(akhir)
	if err != nil {
		fmt.Println("gagal membaca berkas final:", err)
		return
	}
	fmt.Println("rename atomik    : berhasil")
	fmt.Printf("isi data.txt     : %s (%d byte)\n", data, len(data))
}

// tulisAtomik menulis data ke berkas sementara lalu mengganti nama berkas
// final sekaligus, sehingga pembaca tidak pernah melihat isi parsial.
func tulisAtomik(path string, data []byte) error {
	dir := filepath.Dir(path)
	base := filepath.Base(path)

	tmp, err := os.CreateTemp(dir, base+".tmp-*")
	if err != nil {
		return err
	}
	sementara := tmp.Name()

	if _, err := tmp.Write(data); err != nil {
		tmp.Close()
		os.Remove(sementara)
		return err
	}
	// Sync memaksa data turun ke media penyimpanan sebelum rename.
	if err := tmp.Sync(); err != nil {
		tmp.Close()
		os.Remove(sementara)
		return err
	}
	if err := tmp.Close(); err != nil {
		os.Remove(sementara)
		return err
	}
	if err := os.Rename(sementara, path); err != nil {
		os.Remove(sementara)
		return err
	}
	return nil
}
