package main

import (
	"fmt"
	"log/slog"
	"os"
	"strings"
)

// buangWaktu membekukan atribut time agar output demo identik di setiap
// eksekusi; ambang level diteruskan dari pemanggil.
func buangWaktu(level slog.Leveler) *slog.HandlerOptions {
	return &slog.HandlerOptions{
		Level: level,
		ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
			if a.Key == slog.TimeKey && len(groups) == 0 {
				return slog.String(slog.TimeKey, "2026-01-15T08:00:00Z")
			}
			return a
		},
	}
}

// parseLevel menerjemahkan isi LOG_LEVEL menjadi slog.Level.
func parseLevel(teks string) (slog.Level, error) {
	switch strings.ToLower(strings.TrimSpace(teks)) {
	case "debug":
		return slog.LevelDebug, nil
	case "info":
		return slog.LevelInfo, nil
	case "warn", "warning":
		return slog.LevelWarn, nil
	case "error":
		return slog.LevelError, nil
	default:
		return slog.LevelInfo, fmt.Errorf("level tidak dikenal: %q", teks)
	}
}

// levelDariEnv membaca LOG_LEVEL; nilai kosong berarti default Info.
func levelDariEnv() (slog.Level, error) {
	teks := os.Getenv("LOG_LEVEL")
	if teks == "" {
		return slog.LevelInfo, nil
	}
	return parseLevel(teks)
}

// catatSemuaLevel memancarkan satu pesan di setiap level.
func catatSemuaLevel(logger *slog.Logger) {
	logger.Debug("detail validasi", "field", "alamat")
	logger.Info("pesanan diterima", "id", 42)
	logger.Warn("stok menipis", "sisa", 3)
	logger.Error("pembayaran gagal", "id", 42)
}

// simpanPesanan berisi ANTI-POLA: mencatat error lalu mengembalikan error
// yang sama, sehingga pemanggil cenderung mencatatnya ulang.
func simpanPesanan(logger *slog.Logger, id int) error {
	err := fmt.Errorf("koneksi database timeout")
	logger.Error("gagal menyimpan pesanan", "id", id, "err", err)
	return err
}

func main() {
	// Skenario 1: ambang statis dari variabel lingkungan.
	os.Setenv("LOG_LEVEL", "warn")
	ambang, err := levelDariEnv()
	if err != nil {
		panic(err)
	}
	logger := slog.New(slog.NewTextHandler(os.Stdout, buangWaktu(ambang)))

	fmt.Println("--- ambang statis LOG_LEVEL=warn ---")
	catatSemuaLevel(logger)

	// Skenario 2: ambang dinamis dengan slog.LevelVar.
	dinamis := &slog.LevelVar{}
	dinamis.Set(slog.LevelDebug)
	peneliti := slog.New(slog.NewTextHandler(os.Stdout, buangWaktu(dinamis)))

	fmt.Println("--- LevelVar di Debug ---")
	catatSemuaLevel(peneliti)

	dinamis.Set(slog.LevelError)
	fmt.Println("--- LevelVar dinaikkan ke Error ---")
	catatSemuaLevel(peneliti)

	// Skenario 3: anti-pola log + return error.
	fmt.Println("--- anti-pola: error yang sama dicatat dua kali ---")
	if err := simpanPesanan(logger, 42); err != nil {
		logger.Error("gagal menyimpan pesanan", "id", 42, "err", err)
	}
}
