package main

import (
	"encoding/json"
	"fmt"
)

// KontrakV1: bentuk data yang dirilis paling awal.
type KontrakV1 struct {
	ID        string  `json:"id"`
	Pelanggan string  `json:"pelanggan"`
	Total     float64 `json:"total"`
}

// KontrakV2: menambah field metode dan versi tanpa mengubah field lama.
type KontrakV2 struct {
	ID        string  `json:"id"`
	Pelanggan string  `json:"pelanggan"`
	Total     float64 `json:"total"`
	Metode    string  `json:"metode"`
	Versi     int     `json:"versi"`
}

// KontrakV3: field total tidak dipakai lagi, tetapi kontrak tetap bisa
// membaca data lama maupun baru.
type KontrakV3 struct {
	ID        string `json:"id"`
	Pelanggan string `json:"pelanggan"`
	Metode    string `json:"metode"`
	Versi     int    `json:"versi"`
}

func main() {
	// Data lama dibaca struct baru: field baru bernilai zero, tanpa galat.
	dataLama := `{"id":"P-100","pelanggan":"Budi","total":250000}`
	var v2 KontrakV2
	if err := json.Unmarshal([]byte(dataLama), &v2); err != nil {
		fmt.Println("decode gagal:", err)
		return
	}
	fmt.Printf("V2 baca data lama : %+v\n", v2)

	// Data baru dibaca struct lama: field tambahan diabaikan, tanpa galat.
	dataBaru := `{"id":"P-101","pelanggan":"Siti","total":99000,"metode":"transfer","versi":2}`
	var v1 KontrakV1
	if err := json.Unmarshal([]byte(dataBaru), &v1); err != nil {
		fmt.Println("decode gagal:", err)
		return
	}
	fmt.Printf("V1 baca data baru : %+v\n", v1)

	// Data lama tanpa field metode dibaca struct V3: metode kosong.
	var v3 KontrakV3
	if err := json.Unmarshal([]byte(dataLama), &v3); err != nil {
		fmt.Println("decode gagal:", err)
		return
	}
	fmt.Printf("V3 baca data lama : %+v\n", v3)

	// Field versi untuk percabangan penanganan data.
	var v3b KontrakV3
	if err := json.Unmarshal([]byte(dataBaru), &v3b); err != nil {
		fmt.Println("decode gagal:", err)
		return
	}
	switch v3b.Versi {
	case 0:
		fmt.Println("format tanpa nomor versi: perlakukan sebagai V1")
	case 2:
		fmt.Printf("format versi %d: metode %q tersedia\n", v3b.Versi, v3b.Metode)
	}

	// Menulis data: struct lama tetap menghasilkan JSON yang dikenali
	// pembaca lama maupun baru.
	keluaran, _ := json.Marshal(KontrakV2{ID: "P-102", Pelanggan: "Rina", Total: 15000, Metode: "tunai", Versi: 2})
	fmt.Printf("JSON keluaran V2  : %s\n", keluaran)
}
