package main

import (
	"errors"
	"fmt"
	"io/fs"
	"os"
	"path/filepath"
	"testing/fstest"
)

func main() {
	// Direktori kerja sementara agar program aman dijalankan berulang.
	dir, err := os.MkdirTemp("", "contoh-dir-*")
	if err != nil {
		fmt.Println("gagal membuat direktori:", err)
		return
	}
	defer os.RemoveAll(dir)

	// Struktur uji: MkdirAll membuat seluruh jalur bersarang sekaligus.
	if err := os.MkdirAll(filepath.Join(dir, "dokumen", "laporan"), 0755); err != nil {
		fmt.Println("gagal membuat direktori:", err)
		return
	}
	if err := buatBerkas(filepath.Join(dir, "baca-saya.txt"), "mulai dari sini\n"); err != nil {
		fmt.Println("gagal membuat berkas:", err)
		return
	}
	if err := buatBerkas(filepath.Join(dir, "dokumen", "catatan.txt"), "catatan harian\n"); err != nil {
		fmt.Println("gagal membuat berkas:", err)
		return
	}
	if err := buatBerkas(filepath.Join(dir, "dokumen", "laporan", "penjualan.txt"), "omzet: 1000000\n"); err != nil {
		fmt.Println("gagal membuat berkas:", err)
		return
	}

	// os.ReadDir mengembalikan daftar entri terurut berdasarkan nama.
	fmt.Println("isi direktori utama:")
	entries, err := os.ReadDir(dir)
	if err != nil {
		fmt.Println("gagal membaca direktori:", err)
		return
	}
	for _, e := range entries {
		jenis := "berkas"
		if e.IsDir() {
			jenis = "direktori"
		}
		fmt.Printf("  %-14s %s\n", e.Name(), jenis)
	}

	// os.Stat memberikan metadata satu entri.
	target := filepath.Join(dir, "dokumen", "catatan.txt")
	info, err := os.Stat(target)
	if err != nil {
		fmt.Println("gagal membaca metadata:", err)
		return
	}
	fmt.Printf("Stat catatan.txt : %d byte, isDir=%v\n", info.Size(), info.IsDir())

	// Stat mengikuti tautan simbolik; Lstat melaporkan tautannya sendiri.
	if err := os.Symlink(target, filepath.Join(dir, "pintasan.txt")); err != nil {
		fmt.Println("gagal membuat tautan:", err)
		return
	}
	infoStat, err := os.Stat(filepath.Join(dir, "pintasan.txt"))
	if err != nil {
		fmt.Println("gagal membaca metadata:", err)
		return
	}
	infoLstat, err := os.Lstat(filepath.Join(dir, "pintasan.txt"))
	if err != nil {
		fmt.Println("gagal membaca metadata:", err)
		return
	}
	fmt.Printf("Stat  pintasan  : %d byte (mengikuti tautan)\n", infoStat.Size())
	fmt.Printf("Lstat pintasan  : symlink=%v\n", infoLstat.Mode()&fs.ModeSymlink != 0)

	// os.Remove menghapus satu entri.
	if err := os.Remove(filepath.Join(dir, "baca-saya.txt")); err != nil {
		fmt.Println("gagal menghapus berkas:", err)
		return
	}
	_, err = os.Stat(filepath.Join(dir, "baca-saya.txt"))
	fmt.Println("setelah Remove  :", errors.Is(err, fs.ErrNotExist))

	// filepath.WalkDir menelusuri seluruh pohon secara leksikal.
	fmt.Println("pohon di disk:")
	err = filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
		if err != nil {
			return err
		}
		rel, err := filepath.Rel(dir, path)
		if err != nil {
			return err
		}
		if rel == "." {
			return nil
		}
		jenis := "berkas"
		if d.IsDir() {
			jenis = "direktori"
		}
		fmt.Printf("  %-30s %s\n", rel, jenis)
		return nil
	})
	if err != nil {
		fmt.Println("gagal menelusuri pohon:", err)
		return
	}

	// fstest.MapFS adalah filesystem virtual dalam memori.
	peta := fstest.MapFS{
		"proyek/README.md":    {Data: []byte("demo fstest\n")},
		"proyek/main.go":      {Data: []byte("package main\n")},
		"proyek/util/util.go": {Data: []byte("package util\n")},
	}
	fmt.Println("pohon di fstest.MapFS:")
	err = fs.WalkDir(peta, ".", func(path string, d fs.DirEntry, err error) error {
		if err != nil {
			return err
		}
		if path == "." {
			return nil
		}
		jenis := "berkas"
		if d.IsDir() {
			jenis = "direktori"
		}
		fmt.Printf("  %-30s %s\n", path, jenis)
		return nil
	})
	if err != nil {
		fmt.Println("gagal menelusuri pohon:", err)
		return
	}
}

// buatBerkas membuat berkas kecil untuk keperluan demo.
func buatBerkas(path, isi string) error {
	return os.WriteFile(path, []byte(isi), 0644)
}
