package main

import (
	"bytes"
	"fmt"
	"html"
	"html/template"
)

// Produk mendeskripsikan satu item pada katalog toko.
type Produk struct {
	Nama  string
	Harga int
	Tags  []string
	Stok  bool
}

// Katalog adalah data yang diteruskan ke template utama.
type Katalog struct {
	Judul    string
	Items    []Produk
	Komentar string
	Footer   template.HTML
}

func main() {
	const daftar = `<h1>{{.Judul}}</h1>
<ul>
{{range .Items}}<li>{{.Nama}} — {{.Harga | printf "Rp%d"}}{{if .Stok}} (stok tersedia){{else}} (stok habis){{end}}
{{range .Tags}}  <span class="tag">{{.}}</span>
{{end}}</li>
{{else}}<li>Katalog masih kosong.</li>{{end}}
</ul>
<p>Komentar pembeli: {{.Komentar}}</p>
<p>{{.Footer}}</p>`

	// template.Must memanggil panic bila Parse gagal: kesalahan
	// sintaks template sebaiknya menghentikan program sejak awal.
	t := template.Must(template.New("daftar").Parse(daftar))

	data := Katalog{
		Judul: "Katalog Toko Contoh",
		Items: []Produk{
			{Nama: "Buku Go", Harga: 95000, Tags: []string{"buku", "pemrograman"}, Stok: true},
			{Nama: "Mug Keramik", Harga: 45000, Stok: false},
		},
		Komentar: `<script>alert("xss")</script>`,
		// template.HTML menandai markup yang sudah tepercaya;
		// bagian ini TIDAK di-escape saat dirender.
		Footer: template.HTML(`<em>dirender oleh html/template</em>`),
	}

	var buf bytes.Buffer
	if err := t.Execute(&buf, data); err != nil {
		panic(err)
	}
	fmt.Print(buf.String())
	fmt.Println()

	// Data berupa map juga sah; akses field memakai nama kunci.
	const sapaan = `<p>Selamat datang, <b>{{.nama}}</b> — level {{.level}}.</p>
`
	t2 := template.Must(template.New("sapaan").Parse(sapaan))
	dataMap := map[string]string{
		"nama":  "Siti & Budi",
		"level": "gold",
	}

	var buf2 bytes.Buffer
	if err := t2.Execute(&buf2, dataMap); err != nil {
		panic(err)
	}
	fmt.Print(buf2.String())

	fmt.Println("--- html.EscapeString pada string yang sama ---")
	fmt.Println(html.EscapeString(`<script>alert("xss")</script>`))
}
