//go:build dev

package bundler

import (
	"errors"
	"net/http/httptest"
	"os"
	"path/filepath"
	"strings"
	"testing"

	esbuild "github.com/evanw/esbuild/pkg/api"
)

// errorModule must emit a valid ES module that imports the client's
// showErrorOverlay and calls it with the error text + file, so a failed
// transform pops the compile-error overlay instead of just logging.
func TestErrorModuleShape(t *testing.T) {
	src := errorModule("ui/Broken.tsx", errors.New("Unexpected \"}\" at line 3"))

	for _, want := range []string{
		`from "/@hmr/client"`,
		"showErrorOverlay",
		"__hmrShowError(",
		"ui/Broken.tsx",
		`Unexpected`,
	} {
		if !strings.Contains(src, want) {
			t.Errorf("errorModule() missing %q:\n%s", want, src)
		}
	}

	// It must parse as an ES module (the payload is embedded JSON-as-JS).
	res := esbuild.Transform(src, esbuild.TransformOptions{
		Loader: esbuild.LoaderJS, Format: esbuild.FormatESModule, LogLevel: esbuild.LogLevelSilent,
	})
	if len(res.Errors) > 0 {
		msgs := esbuild.FormatMessages(res.Errors, esbuild.FormatMessagesOptions{})
		t.Fatalf("errorModule() is not valid JS:\n%s\n--- source ---\n%s", strings.Join(msgs, "\n"), src)
	}
}

// A source file that fails to compile must be served as the overlay stub (HTTP
// 200, not a 500 that would tear the module graph), so the browser shows the
// error and the next save can recover.
func TestServeModuleCompileErrorServesOverlay(t *testing.T) {
	root, err := filepath.Abs("../..")
	if err != nil {
		t.Fatal(err)
	}
	t.Chdir(root)
	frontendAbs, _ := filepath.Abs(frontendDir)

	tmpSrc := evalSymlinks(t.TempDir())
	// Deliberately broken JSX: an unclosed tag the compiler/esbuild must reject.
	os.WriteFile(filepath.Join(tmpSrc, "Broken.tsx"),
		[]byte("export default function Broken() {\n  return <div><span></div>;\n}\n"), 0o644)

	eps, _ := loadVendorManifest(filepath.Join(frontendAbs, "vendor"))
	d := &devServer{hub: newHub(), frontend: frontendAbs, srcRoot: tmpSrc,
		vendor: filepath.Join(frontendAbs, "vendor"), graph: newModuleGraph(),
		vendorCache: map[string][]byte{}, cssTrigger: make(chan struct{}, 1), vendorEntrypoints: eps}

	req := httptest.NewRequest("GET", srcURLPrefix+"Broken.tsx", nil)
	w := httptest.NewRecorder()
	d.serveModule(w, req)

	if w.Code != 200 {
		t.Fatalf("broken module served status %d, want 200 (overlay stub)", w.Code)
	}
	body := w.Body.String()
	for _, want := range []string{"showErrorOverlay", `from "/@hmr/client"`, "Broken.tsx"} {
		if !strings.Contains(body, want) {
			t.Errorf("overlay stub missing %q:\n%s", want, body)
		}
	}
}
