package vdom

import "encoding/json"

// Schedule is installed by the wasm runtime at startup; a signal write
// triggers a re-render. On the server it is nil (renders are one-shot).
var Schedule func()

// Signal holds state. On the client, writing it schedules a re-render.
type Signal[T any] struct{ v T }

func NewSignal[T any](initial T) *Signal[T] {
	s := &Signal[T]{v: initial}
	if active != nil {
		active.adopt(s) // server component round-trip: restore prior value, track for snapshot
	}
	return s
}

func (s *Signal[T]) Get() T { return s.v }

func (s *Signal[T]) Set(v T) {
	s.v = v
	if Schedule != nil {
		Schedule()
	}
}

func (s *Signal[T]) Update(fn func(T) T) { s.Set(fn(s.v)) }

func (s *Signal[T]) snapshot() []byte     { b, _ := json.Marshal(s.v); return b }
func (s *Signal[T]) restoreFrom(b []byte) { _ = json.Unmarshal(b, &s.v) }

// ---- signal-state round-trip for server components ----
//
// A server component is stateless on the server: its signal values are
// serialized and round-tripped through the client. While a Collector is active
// (during a server-component render on the server), NewSignal restores each
// signal's value from the incoming snapshot by creation order (hook-order), and
// remembers it so the new values can be snapshotted back out.

type signalState interface {
	snapshot() []byte
	restoreFrom([]byte)
}

// Collector captures the signals created during a server render.
type Collector struct {
	restore [][]byte
	idx     int
	sigs    []signalState
}

var active *Collector

func (c *Collector) adopt(s signalState) {
	if c.idx < len(c.restore) {
		s.restoreFrom(c.restore[c.idx])
	}
	c.idx++
	c.sigs = append(c.sigs, s)
}

// BeginCollect starts collecting signals, restoring them from `restore` (which
// may be nil for an initial mount). Call EndCollect when construction is done.
func BeginCollect(restore [][]byte) *Collector {
	active = &Collector{restore: restore}
	return active
}

// EndCollect stops collecting (rendering/handlers may still read the signals).
func EndCollect() { active = nil }

// Snapshot returns the current values of the collected signals, in order.
func (c *Collector) Snapshot() [][]byte {
	out := make([][]byte, len(c.sigs))
	for i, s := range c.sigs {
		out[i] = s.snapshot()
	}
	return out
}
