//go:build js && wasm

package wasmruntime

import (
	"strings"
	"syscall/js"
	"testing"

	"kjol/wasmruntime/vdom"
)

// buildServerDOM fakes what the HTML parser hands us: a real DOM tree, built from
// the SERVER's VNode tree, which hydration then adopts.
func buildServerDOM(server *vdom.VNode) js.Value { return createDOM(server, "") }

// Hydration ADOPTS the server's DOM. If the server's HTML and the client's render
// disagree, the disagreement has to be resolved in the CLIENT's favour — it is the
// one that is running, and it is the only one that can still change.
//
// Left unreconciled, the divergence is permanent and invisible: the DOM keeps the
// server's value, and no later re-render corrects it either, because updateAttrs
// compares one client VNode against the next — which agree with each other and
// disagree with the DOM. The symptom is a page that is right when you navigate to it
// and wrong when you refresh onto it.
func TestHydrationReconcilesAttributes(t *testing.T) {
	root := document.Call("createElement", "div")

	// What the server rendered (say, from a stale binary).
	server := vdom.El("main", vdom.Attr("class", "mx-auto max-w-5xl px-4"))
	root.Call("appendChild", buildServerDOM(server))

	// What the client actually renders now.
	client := vdom.El("main", vdom.Attr("class", "mx-auto max-w-[100rem] px-4"))
	hydrateNode(root.Get("firstChild"), client)

	got := root.Get("firstChild").Call("getAttribute", "class").String()
	if got != "mx-auto max-w-[100rem] px-4" {
		t.Errorf("after hydration class = %q, want the client's value — the server's stale class was kept", got)
	}
}

// And the ordinary case must still work: matching markup is adopted, not rebuilt.
func TestHydrationAdoptsMatchingMarkup(t *testing.T) {
	root := document.Call("createElement", "div")

	server := vdom.El("div", vdom.Attr("class", "a"),
		vdom.El("span", vdom.Text("hello")),
	)
	root.Call("appendChild", buildServerDOM(server))
	adopted := root.Get("firstChild")

	client := vdom.El("div", vdom.Attr("class", "a"),
		vdom.El("span", vdom.Text("hello")),
	)
	hydrateNode(root.Get("firstChild"), client)

	// Same node — hydration adopted it rather than replacing it.
	if !rt(client).dom.Equal(adopted) {
		t.Error("hydration replaced a matching node instead of adopting it")
	}
	if got := root.Get("innerHTML").String(); !strings.Contains(got, "hello") {
		t.Errorf("content lost: %s", got)
	}
}

// A text node that disagrees is corrected too (this already worked; pin it).
func TestHydrationCorrectsText(t *testing.T) {
	root := document.Call("createElement", "div")

	server := vdom.El("p", vdom.Text("stale"))
	root.Call("appendChild", buildServerDOM(server))

	client := vdom.El("p", vdom.Text("fresh"))
	hydrateNode(root.Get("firstChild"), client)

	if got := root.Get("innerHTML").String(); !strings.Contains(got, "fresh") {
		t.Errorf("stale server text survived hydration: %s", got)
	}
}
