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

package app

import (
	"bytes"
	"strconv"
	"time"

	chart "github.com/wcharczuk/go-chart/v2"

	. "kjol/vdom"
)

// ServerCounter is a SERVER component — note it's written exactly like a client
// component (same builders, signals, On handlers). The //gowasm:server directive
// makes the build generate a client stub so calling ServerCounter() on the
// frontend is identical to calling any component; the state and this render run
// on the server (its chart is computed there with go-chart), and clicks
// round-trip over /rsc.
//
// The chart plots the counter value against the wall-clock time of each click
// (milliseconds since the first click), so spacing clicks out spreads the data
// points along the x-axis. Because the component is stateless on the server, the
// click points live in a signal that round-trips with the rest of its state
// (a plain slice would reset on every request).
//
//gowasm:server
func ServerCounter() func() *VNode {
	count := NewSignal(0)
	points := NewSignal([]clickPoint{})
	bump := func(delta int) {
		count.Set(count.Get() + delta)
		points.Set(append(points.Get(), clickPoint{T: time.Now().UnixMilli(), V: count.Get()}))
	}
	return func() *VNode {
		return Div(Attr("class", "card"),
			Div(Attr("class", "card-body"),
				Div(Attr("class", "d-flex align-items-center gap-2 mb-2"),
					Span(Text("Server counter: ")),
					Strong(Attr("class", "badge text-bg-success fs-6"), Text(strconv.Itoa(count.Get()))),
					Button(Attr("class", "btn btn-sm btn-outline-secondary"),
						On(EVENT_CLICK, func() { bump(-1) }), Text("−")),
					Button(Attr("class", "btn btn-sm btn-success"),
						On(EVENT_CLICK, func() { bump(1) }), Text("+")),
				),
				Div(Attr("class", "border rounded p-2 bg-white overflow-auto"),
					Raw(clickChartSVG(points.Get()))),
			),
		)
	}
}

// clickPoint records one click: its wall-clock time and the resulting counter
// value. Exported fields so the signal's JSON snapshot round-trips it.
type clickPoint struct {
	T int64 // click time, Unix milliseconds
	V int   // counter value after the click
}

// clickChartSVG plots counter value vs. time-of-click (ms since the first
// click) as a line graph. Explicit axis ranges keep it valid for the tricky
// cases (a single click, or several clicks within the same millisecond).
func clickChartSVG(points []clickPoint) string {
	if len(points) == 0 {
		return `<span class="text-muted">Click + / − to plot the counter over time (ms since the first click).</span>`
	}
	t0 := points[0].T
	xs := make([]float64, len(points))
	ys := make([]float64, len(points))
	minY, maxY := 0.0, 0.0 // keep the zero baseline in view for context
	for i, p := range points {
		xs[i] = float64(p.T - t0)
		ys[i] = float64(p.V)
		if ys[i] < minY {
			minY = ys[i]
		}
		if ys[i] > maxY {
			maxY = ys[i]
		}
	}
	maxX := xs[len(xs)-1]
	if maxX <= 0 {
		maxX = 1 // rapid or single clicks: avoid a zero-width x-range
	}
	if minY == maxY {
		maxY++ // avoid a zero-height y-range
	}
	graph := chart.Chart{
		Title:      "Counter over time (computed on the server)",
		TitleStyle: chart.Style{FontSize: 14},
		Background: chart.Style{Padding: chart.Box{Top: 48, Left: 20, Right: 20, Bottom: 40}},
		Height:     260,
		XAxis: chart.XAxis{
			Name:  "ms since first click",
			Range: &chart.ContinuousRange{Min: 0, Max: maxX},
		},
		YAxis: chart.YAxis{
			Name:  "counter",
			Range: &chart.ContinuousRange{Min: minY, Max: maxY},
		},
		Series: []chart.Series{
			chart.ContinuousSeries{
				XValues: xs,
				YValues: ys,
				Style: chart.Style{
					StrokeColor: chart.ColorGreen, StrokeWidth: 2,
					DotColor: chart.ColorGreen, DotWidth: 4, // a dot at each click
				},
			},
		},
	}
	var buf bytes.Buffer
	if graph.Render(chart.SVG, &buf) != nil {
		return `<span class="text-danger">chart error</span>`
	}
	return buf.String()
}
