//go:build !(js && wasm)

package rsc

import (
	"encoding/gob"
	"net/http"
	"sort"
	"sync"

	"kjol/vdom"
)

// registry maps a server-component name to its factory (created by generated
// code — one Register per //gowasm:server component). The factory builds the
// component's signals + render closure; here it's re-run per request (stateless).
var registry = map[string]func() func() *vdom.VNode{}

func Register(name string, factory func() func() *vdom.VNode) { registry[name] = factory }

// renderMu serializes server renders because the vdom signal Collector is a
// process-global (see vdom.BeginCollect). Fine for this scale.
var renderMu sync.Mutex

// Handler is the single /rsc endpoint. It restores the component's signals from
// the request, applies the event (if any), re-renders, and returns the new
// signal snapshot + tree.
func Handler(w http.ResponseWriter, r *http.Request) {
	var req Request
	if gob.NewDecoder(r.Body).Decode(&req) != nil {
		http.Error(w, "bad request", http.StatusBadRequest)
		return
	}
	factory := registry[req.Name]
	if factory == nil {
		http.NotFound(w, r)
		return
	}

	renderMu.Lock()
	c := vdom.BeginCollect(req.State) // restore prior signal values (nil on mount)
	render := factory()               // signals created + restored here
	vdom.EndCollect()

	if req.Kind == "event" {
		// Reproduce the tree the client currently shows (same state) to find the
		// handler by node id, then invoke it (mutating signals).
		_, handlers := renderIDs(render())
		if hm := handlers[req.NodeID]; hm != nil {
			if h := hm[req.Event]; h != nil {
				h(serverEvent{value: req.Value})
			}
		}
	}
	tree, _ := renderIDs(render()) // render the result (reflects the mutation)
	resp := Response{State: c.Snapshot(), Tree: tree}
	renderMu.Unlock()

	w.Header().Set("Content-Type", "application/octet-stream")
	_ = gob.NewEncoder(w).Encode(resp)
}

// renderIDs serializes a VNode tree to SNode, assigning a stable (traversal
// order) id to each node with handlers and building the handler table.
func renderIDs(root *vdom.VNode) (*SNode, map[int]map[string]func(vdom.Event)) {
	handlers := map[int]map[string]func(vdom.Event){}
	id := 0
	var walk func(*vdom.VNode) *SNode
	walk = func(v *vdom.VNode) *SNode {
		sn := &SNode{Tag: v.Tag, Text: v.Text, HTML: v.HTML, Attrs: v.Attrs}
		if len(v.Events) > 0 {
			id++
			sn.ID = id
			hm := map[string]func(vdom.Event){}
			for ev, h := range v.Events {
				sn.Events = append(sn.Events, ev)
				hm[ev] = h
			}
			sort.Strings(sn.Events)
			handlers[id] = hm
		}
		if v.HTML == "" {
			for _, k := range v.Children {
				sn.Kids = append(sn.Kids, walk(k))
			}
		}
		return sn
	}
	return walk(root), handlers
}

type serverEvent struct{ value string }

func (e serverEvent) PreventDefault() {}
func (e serverEvent) Value() string   { return e.value }
