package handlers

import (
	"io/fs"
	"log/slog"
	"mime"
	"net/http"
	"path"
	"path/filepath"
	"strings"

	"github.com/labstack/echo/v4"

	"github.com/memohai/memoh/internal/embedded"
)

type EmbeddedWebHandler struct {
	log   *slog.Logger
	webFS fs.FS
}

var embeddedStaticRoutes = map[string]struct {
	assetPath   string
	contentType string
}{
	"/logo.png":               {assetPath: "logo.png", contentType: "image/png"},
	"/channels/telegram.webp": {assetPath: "channels/telegram.webp", contentType: "image/webp"},
	"/channels/feishu.png":    {assetPath: "channels/feishu.png", contentType: "image/png"},
}

func NewEmbeddedWebHandler(log *slog.Logger) (*EmbeddedWebHandler, error) {
	webFS, err := embedded.WebFS()
	if err != nil {
		return nil, err
	}
	return &EmbeddedWebHandler{log: log, webFS: webFS}, nil
}

func (h *EmbeddedWebHandler) Register(e *echo.Echo) {
	e.GET("/assets/*", h.serveAsset)
	for route, meta := range embeddedStaticRoutes {
		e.GET(route, h.serveStatic(meta.assetPath, meta.contentType))
	}
	e.GET("/", h.serveIndex)
	e.GET("/*", func(c echo.Context) error {
		reqPath := c.Request().URL.Path
		if isBackendPath(reqPath) || strings.Contains(path.Base(reqPath), ".") {
			return echo.ErrNotFound
		}
		return h.serveIndex(c)
	})
}

func (h *EmbeddedWebHandler) serveIndex(c echo.Context) error {
	return h.serveKnownGzip(c, "index.html", "text/html; charset=utf-8")
}

func (h *EmbeddedWebHandler) serveStatic(targetPath, contentType string) echo.HandlerFunc {
	return func(c echo.Context) error {
		return h.serveKnownGzip(c, targetPath, contentType)
	}
}

func (h *EmbeddedWebHandler) serveAsset(c echo.Context) error {
	assetPath := strings.TrimPrefix(c.Param("*"), "/")
	if assetPath == "" {
		return echo.ErrNotFound
	}

	fullPath := path.Join("assets", assetPath)
	contentType := mime.TypeByExtension(filepath.Ext(assetPath))
	if contentType == "" {
		contentType = "application/octet-stream"
	}
	gzipPath := fullPath + ".gz"
	content, err := fs.ReadFile(h.webFS, gzipPath)
	if err != nil {
		return echo.ErrNotFound
	}
	header := c.Response().Header()
	header.Set(echo.HeaderContentEncoding, "gzip")
	header.Set(echo.HeaderVary, "Accept-Encoding")
	return c.Blob(http.StatusOK, contentType, content)
}

func (h *EmbeddedWebHandler) serveKnownGzip(c echo.Context, targetPath, contentType string) error {
	gzipPath := targetPath + ".gz"
	content, err := fs.ReadFile(h.webFS, gzipPath)
	if err != nil {
		if targetPath == "index.html" {
			h.log.Error("read embedded index.html.gz failed", slog.Any("error", err))
		}
		return echo.ErrNotFound
	}
	header := c.Response().Header()
	header.Set(echo.HeaderContentEncoding, "gzip")
	header.Set(echo.HeaderVary, "Accept-Encoding")
	return c.Blob(http.StatusOK, contentType, content)
}

func isBackendPath(p string) bool {
	return p == "/ping" ||
		p == "/health" ||
		strings.HasPrefix(p, "/api") ||
		strings.HasPrefix(p, "/auth") ||
		strings.HasPrefix(p, "/channels") ||
		strings.HasPrefix(p, "/containers")
}
