package main

import (
	"fmt"
	"reflect"
)

type Limit int16

type Config struct {
	Limit  Limit `json:"limit"`
	secret string
}

func setLimit(dst any, n int64) error {
	v := reflect.ValueOf(dst)
	if !v.IsValid() || v.Kind() != reflect.Pointer || v.IsNil() {
		return fmt.Errorf("need a non-nil pointer")
	}
	v = v.Elem()
	if v.Kind() != reflect.Struct {
		return fmt.Errorf("need a pointer to struct")
	}
	sf, ok := v.Type().FieldByName("Limit")
	if !ok {
		return fmt.Errorf("missing Limit field")
	}
	f, err := v.FieldByIndexErr(sf.Index)
	if err != nil {
		return fmt.Errorf("cannot reach Limit: %w", err)
	}
	if !f.IsValid() || !f.CanSet() || f.Kind() != reflect.Int16 {
		return fmt.Errorf("need a settable int16 Limit field")
	}
	if f.OverflowInt(n) {
		return fmt.Errorf("Limit overflows int16: %d", n)
	}
	f.SetInt(n)
	return nil
}

func panicText(f func()) (text string) {
	defer func() {
		if r := recover(); r != nil {
			text = fmt.Sprint(r)
		}
	}()
	f()
	return "no panic"
}

func main() {
	c := Config{Limit: 80, secret: "token"}
	t := reflect.TypeOf(c.Limit)
	fmt.Println("type:", t, "kind:", t.Kind(), "same as int16:", t == reflect.TypeFor[int16]())
	fmt.Println("tag:", reflect.TypeOf(c).Field(0).Tag.Get("json"))
	copy := reflect.ValueOf(c)
	ptr := reflect.ValueOf(&c)
	field := ptr.Elem().FieldByName("Limit")
	fmt.Println("value:", copy.CanAddr(), copy.CanSet())
	fmt.Println("pointer:", ptr.CanAddr(), ptr.CanSet())
	fmt.Println("field:", field.CanAddr(), field.CanSet())
	private := ptr.Elem().FieldByName("secret")
	fmt.Println("private:", private.CanAddr(), private.CanSet(), private.CanInterface())
	fmt.Println("set:", setLimit(&c, 8080), c.Limit)
	fmt.Println("overflow:", setLimit(&c, 40000), c.Limit)
	fmt.Println("panic:", panicText(func() { copy.FieldByName("Limit").SetInt(90) }))
	var p *Config
	empty, typedNil := reflect.ValueOf(nil), reflect.ValueOf(p)
	fmt.Println("nil:", empty.IsValid(), empty.Kind())
	fmt.Println("typed nil:", typedNil.IsValid(), typedNil.Kind(), typedNil.IsNil())
	m := map[string]int{"n": 1}
	mv, key := reflect.ValueOf(m), reflect.ValueOf("n")
	fmt.Println("map entry settable:", mv.MapIndex(key).CanSet())
	mv.SetMapIndex(key, reflect.ValueOf(2))
	fmt.Println("map updated:", m["n"])
}
