package templ_test

import (
	"context"
	"fmt"
	"strings"
	"testing"

	"github.com/a-h/templ"
	"github.com/google/go-cmp/cmp"
)

type onceHandleTest struct {
	ctx      context.Context
	expected string
}

func TestOnceHandle(t *testing.T) {
	withHello := templ.WithChildren(context.Background(), templ.Raw("hello"))
	tests := []struct {
		name  string
		tests []onceHandleTest
	}{
		{
			name: "renders nothing without children",
			tests: []onceHandleTest{
				{
					ctx:      context.Background(),
					expected: "",
				},
			},
		},
		{
			name: "children are rendered",
			tests: []onceHandleTest{
				{
					ctx:      templ.WithChildren(context.Background(), templ.Raw("hello")),
					expected: "hello",
				},
			},
		},
		{
			name: "children are rendered once per context",
			tests: []onceHandleTest{
				{
					ctx:      withHello,
					expected: "hello",
				},
				{
					ctx:      withHello,
					expected: "",
				},
			},
		},
		{
			name: "different contexts have different once state",
			tests: []onceHandleTest{
				{
					ctx:      templ.WithChildren(context.Background(), templ.Raw("hello")),
					expected: "hello",
				},
				{
					ctx:      templ.WithChildren(context.Background(), templ.Raw("hello2")),
					expected: "hello2",
				},
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			c := templ.NewOnceHandle().Once()
			for i, test := range tt.tests {
				t.Run(fmt.Sprintf("render %d/%d", i+1, len(tt.tests)), func(t *testing.T) {
					html, err := templ.ToGoHTML(test.ctx, c)
					if err != nil {
						t.Fatalf("unexpected error: %v", err)
					}
					if diff := cmp.Diff(test.expected, string(html)); diff != "" {
						t.Errorf("unexpected diff:\n%v", diff)
					}
				})
			}
		})
	}
	t.Run("each new handle manages different state", func(t *testing.T) {
		ctx := templ.WithChildren(context.Background(), templ.Raw("hello"))
		h1 := templ.NewOnceHandle()
		c1 := h1.Once()
		h2 := templ.NewOnceHandle()
		c2 := h2.Once()
		c3 := h2.Once()
		var w strings.Builder
		if err := c1.Render(ctx, &w); err != nil {
			t.Fatalf("unexpected error: %v", err)
		}
		if err := c2.Render(ctx, &w); err != nil {
			t.Fatalf("unexpected error: %v", err)
		}
		if err := c3.Render(ctx, &w); err != nil {
			t.Fatalf("unexpected error: %v", err)
		}
		if diff := cmp.Diff("hellohello", w.String()); diff != "" {
			t.Errorf("unexpected diff:\n%v", diff)
		}
	})
	t.Run("a handle can be used to render a specific component", func(t *testing.T) {
		ctx := templ.WithChildren(context.Background(), templ.Raw("child"))
		o := templ.NewOnceHandle(templ.WithComponent(templ.Raw("c"))).Once()
		var w strings.Builder
		if err := o.Render(ctx, &w); err != nil {
			t.Fatalf("unexpected error: %v", err)
		}
		if diff := cmp.Diff("c", w.String()); diff != "" {
			t.Errorf("unexpected diff:\n%v", diff)
		}
	})
}