package static

import (
	"io/fs"
	"net/http"
	"path"
	"strings"

	"github.com/gin-gonic/gin"
)

// ServeEmbedded serves embedded static files with SPA fallback.
func ServeEmbedded(staticFS fs.FS, subDir string) gin.HandlerFunc {
	subFS, err := fs.Sub(staticFS, subDir)
	if err != nil {
		panic("failed to get sub filesystem: " + err.Error())
	}

	fileServer := http.FileServer(http.FS(subFS))

	return func(c *gin.Context) {
		urlPath := c.Request.URL.Path

		if strings.HasPrefix(urlPath, "/api") || strings.HasPrefix(urlPath, "/ws") {
			c.Next()
			return
		}

		// Normalize and reject any path that escapes the static root.
		// io/fs refuses `..` segments at Open time, but rejecting here
		// avoids any chance of an unintended fileServer redirect.
		clean := path.Clean(urlPath)
		if !strings.HasPrefix(clean, "/") || strings.HasPrefix(clean, "/..") || clean == ".." {
			c.AbortWithStatus(http.StatusBadRequest)
			return
		}

		// Existence probe — when the file exists, defer to net/http.FileServer
		// (which handles ETags, content-type, conditional GETs). When it
		// doesn't, fall back to SPA-style index.html.
		probe := strings.TrimPrefix(clean, "/")
		if probe == "" {
			probe = "index.html"
		}
		if entry, err := subFS.Open(probe); err == nil {
			info, statErr := entry.Stat()
			entry.Close()
			// Refuse directory requests other than the root so we
			// don't accidentally serve an autogenerated index listing.
			if statErr == nil && (!info.IsDir() || clean == "/") {
				fileServer.ServeHTTP(c.Writer, c.Request)
				c.Abort()
				return
			}
		}

		// SPA fallback: hand back index.html for any unknown route.
		c.Request.URL.Path = "/"
		fileServer.ServeHTTP(c.Writer, c.Request)
		c.Abort()
	}
}
