package main

import (
	"bytes"
	"fmt"
	"testing/fstest"
	"text/template"
)

// Halaman adalah data yang diteruskan ke seluruh template komposisi.
type Halaman struct {
	Judul string
	Nama  string
}

// partials berisi dua template kecil yang dipakai ulang di banyak halaman.
// {{define}} menamai potongan; aksi ini tidak mencetak apa pun.
const partials = `{{define "header"}}== {{.Judul}} =={{end}}{{define "footer"}}-- copyright Toko Contoh --{{end}}`

// layoutSrc adalah kerangka halaman; block "content" memberi
// konten default yang dapat ditimpa halaman turunan.
const layoutSrc = `{{template "header" .}}
{{block "content" .}}(halaman tanpa konten){{end}}
{{template "footer" .}}`

// render mengeksekusi template utama dan mengembalikan hasilnya.
func render(t *template.Template, data Halaman) string {
	var buf bytes.Buffer
	if err := t.Execute(&buf, data); err != nil {
		panic(err)
	}
	return buf.String()
}

// renderNama mengeksekusi template bernama dari sebuah set.
func renderNama(t *template.Template, nama string, data Halaman) string {
	var buf bytes.Buffer
	if err := t.ExecuteTemplate(&buf, nama, data); err != nil {
		panic(err)
	}
	return buf.String()
}

func main() {
	berandaData := Halaman{Judul: "Beranda", Nama: "Budi"}
	tentangData := Halaman{Judul: "Tentang", Nama: "Budi"}

	// Bagian 1: satu set template dari dua panggilan Parse.
	const berandaSrc = `{{template "header" .}}
Selamat datang, {{.Nama}}.
{{template "footer" .}}`

	set := template.Must(template.New("beranda").Parse(partials))
	template.Must(set.Parse(berandaSrc))

	fmt.Println("=== 1. define + template ===")
	fmt.Println(render(set, berandaData))

	// Bagian 2: layout dengan Clone lalu timpa block "content".
	base := template.Must(template.New("layout").Parse(partials))
	template.Must(base.Parse(layoutSrc))

	berandaT := template.Must(template.Must(base.Clone()).Parse(
		`{{define "content"}}Selamat datang, {{.Nama}}.{{end}}`))
	tentangT := template.Must(template.Must(base.Clone()).Parse(
		`{{define "content"}}Toko Contoh menjual buku dan merchandise Go.{{end}}`))

	fmt.Println("=== 2. block: default lalu override ===")
	fmt.Println("[base]    " + render(base, berandaData))
	fmt.Println("[beranda] " + render(berandaT, berandaData))
	fmt.Println("[tentang] " + render(tentangT, tentangData))

	// Bagian 3: ParseFS — setiap set dibangun dari base + satu halaman.
	// fstest.MapFS meniru folder berkas template sungguhan sehingga
	// contoh tetap deterministik tanpa membaca berkas dari disk.
	fsys := fstest.MapFS{
		"layout/base.tmpl":   &fstest.MapFile{Data: []byte(partials + layoutSrc)},
		"pages/beranda.tmpl": &fstest.MapFile{Data: []byte(`{{define "content"}}Selamat datang, {{.Nama}}.{{end}}`)},
		"pages/tentang.tmpl": &fstest.MapFile{Data: []byte(`{{define "content"}}Toko Contoh menjual buku dan merchandise Go.{{end}}`)},
	}

	fmt.Println("=== 3. ParseFS dari berkas ===")
	halaman := []struct {
		berkas string
		data   Halaman
	}{
		{"pages/beranda.tmpl", berandaData},
		{"pages/tentang.tmpl", tentangData},
	}
	for _, h := range halaman {
		ts := template.Must(template.ParseFS(fsys, "layout/base.tmpl", h.berkas))
		fmt.Printf("[base.tmpl + %s]\n%s\n", h.berkas, renderNama(ts, "base.tmpl", h.data))
	}
}
