package sandbox

import (
	"sort"
	"strconv"
)

// SchemaOption is one sandbox option as the UI needs it: identity, type, the
// selectable values (with display labels), and the default value.
type SchemaOption struct {
	Name     string   `json:"name"`     // SandboxOptions enum member (stable key + config_values suffix)
	Display  string   `json:"display"`  // human label
	Category string   `json:"category"` // raw category key (General/Entities/...)
	Type     string   `json:"type"`     // "float" | "int" | "bool"
	Values   []string `json:"values"`   // selectable values in index order (the code stores the index)
	Labels   []string `json:"labels"`   // optional UI labels parallel to Values (may be empty)
	Default  string   `json:"default"`  // default value (one of Values)
}

// SchemaCategory groups options under a UI-friendly title.
type SchemaCategory struct {
	Key     string         `json:"key"`     // raw category key
	Title   string         `json:"title"`   // UI title
	Icon    string         `json:"icon"`    // emoji for the section
	Options []SchemaOption `json:"options"` // in registration order
}

// categoryMeta maps the decompile's raw category keys to UI titles + icons,
// in display order.
var categoryMeta = []struct{ Key, Title, Icon string }{
	{"General", "Player", "🧍"},
	{"Entities", "Entities & Zombies", "🧟"},
	{"World", "World & Blood Moon", "🌍"},
	{"Resources", "Loot & Resources", "💰"},
	{"Crafting", "Crafting", "🔨"},
	{"Traders", "Traders", "🪙"},
	{"Tasks", "Quests & Tasks", "📋"},
	{"Misc", "Miscellaneous", "🎛️"},
}

// Schema returns the full 150-option sandbox schema grouped into the 8 UI
// categories, in display order. The UI renders per-option controls from this and
// sends back a name→value map for Encode.
func Schema() []SchemaCategory {
	byCat := map[string][]SchemaOption{}
	for i := range options {
		o := &options[i]
		so := SchemaOption{
			Name:     o.Name,
			Display:  o.Display,
			Category: o.Category,
			Default:  o.valueAtIndex(defaultIdx[o.Name]),
		}
		switch o.Type {
		case optFloat:
			so.Type = "float"
			for _, f := range o.set.Floats {
				so.Values = append(so.Values, strconv.FormatFloat(f, 'f', -1, 64))
			}
		case optInt:
			so.Type = "int"
			for _, n := range o.set.Ints {
				so.Values = append(so.Values, strconv.Itoa(n))
			}
		case optBool:
			so.Type = "bool"
			for _, b := range o.set.Bools {
				so.Values = append(so.Values, strconv.FormatBool(b))
			}
		}
		so.Labels = append(so.Labels, o.set.Display...)
		byCat[o.Category] = append(byCat[o.Category], so)
	}
	var out []SchemaCategory
	for _, cm := range categoryMeta {
		out = append(out, SchemaCategory{Key: cm.Key, Title: cm.Title, Icon: cm.Icon, Options: byCat[cm.Key]})
	}
	// Any category not in categoryMeta (forward-compat) appended at the end.
	known := map[string]bool{}
	for _, cm := range categoryMeta {
		known[cm.Key] = true
	}
	var extra []string
	for k := range byCat {
		if !known[k] {
			extra = append(extra, k)
		}
	}
	sort.Strings(extra)
	for _, k := range extra {
		out = append(out, SchemaCategory{Key: k, Title: k, Icon: "⚙️", Options: byCat[k]})
	}
	return out
}
