package static

import (
	"net/http"
	"os"
	"path/filepath"
	"strings"

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

// Register attaches the static file handler to the Gin router.
// distDir is the path to frontend/dist/.
func Register(r *gin.Engine, distDir string) {
	r.NoRoute(func(c *gin.Context) {
		path := c.Request.URL.Path

		// Skip API routes – they're handled elsewhere
		if strings.HasPrefix(path, "/api/") {
			c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
			return
		}

		// Try to serve a real file
		fsPath := filepath.Join(distDir, filepath.FromSlash(path))
		if info, err := os.Stat(fsPath); err == nil && !info.IsDir() {
			serveFile(c, fsPath)
			return
		}

		// SPA fallback: serve index.html
		indexPath := filepath.Join(distDir, "index.html")
		if _, err := os.Stat(indexPath); err != nil {
			c.String(http.StatusNotFound, "frontend not built – run npm run build:web in frontend/")
			return
		}
		c.Header("Cache-Control", "no-cache, no-store, must-revalidate")
		c.File(indexPath)
	})
}

func serveFile(c *gin.Context, fsPath string) {
	name := filepath.Base(fsPath)
	ext := strings.ToLower(filepath.Ext(name))

	if name == "index.html" || strings.HasSuffix(name, ".webmanifest") {
		c.Header("Cache-Control", "no-cache, no-store, must-revalidate")
	} else if isHashedAsset(name, ext) {
		c.Header("Cache-Control", "public, max-age=31536000, immutable")
	} else if ext == ".woff" || ext == ".woff2" || ext == ".ttf" || ext == ".otf" {
		c.Header("Cache-Control", "public, max-age=31536000, immutable")
	} else {
		c.Header("Cache-Control", "public, max-age=3600")
	}

	// Gin's File() handles Content-Type and ETag automatically
	c.File(fsPath)
}

// isHashedAsset returns true if the filename looks like a Vite hashed asset
// (e.g. index-BxYt3k2a.js, chunk-abc123.css).
func isHashedAsset(name, ext string) bool {
	switch ext {
	case ".js", ".css", ".mjs":
		return strings.Contains(name, "-") || strings.Contains(name, ".")
	}
	return false
}
