package zoraxy_plugin

import (
	"embed"
	"io/fs"
	"net/http"
	"net/url"
	"os"
	"strings"
	"time"
)

// PluginUiRouter serves an embedded web UI and provides CSRF token injection.
type PluginUiRouter struct {
	PluginID         string
	TargetFs         *embed.FS
	TargetFsPrefix   string
	HandlerPrefix    string
	EnableDebug      bool
	terminateHandler func()
}

// NewPluginEmbedUIRouter creates a router backed by an embed.FS.
// targetFsPrefix is the root folder within the embed.FS (e.g. "/web").
// handlerPrefix is the HTTP path prefix (e.g. "/ui").
func NewPluginEmbedUIRouter(pluginID string, targetFs *embed.FS, targetFsPrefix string, handlerPrefix string) *PluginUiRouter {
	if !strings.HasPrefix(targetFsPrefix, "/") {
		targetFsPrefix = "/" + targetFsPrefix
	}
	targetFsPrefix = strings.TrimSuffix(targetFsPrefix, "/")

	if !strings.HasPrefix(handlerPrefix, "/") {
		handlerPrefix = "/" + handlerPrefix
	}
	handlerPrefix = strings.TrimSuffix(handlerPrefix, "/")

	return &PluginUiRouter{
		PluginID:       pluginID,
		TargetFs:       targetFs,
		TargetFsPrefix: targetFsPrefix,
		HandlerPrefix:  handlerPrefix,
	}
}

// csrfMiddleware intercepts HTML responses and injects the CSRF token.
func (p *PluginUiRouter) csrfMiddleware(r *http.Request, fsHandler http.Handler) http.Handler {
	csrfToken := r.Header.Get("X-Zoraxy-Csrf")
	if csrfToken == "" {
		csrfToken = "missing-csrf-token"
	}

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if strings.HasSuffix(r.URL.Path, ".html") {
			targetPath := p.TargetFsPrefix + "/" + strings.TrimPrefix(r.URL.Path, "/")
			targetPath = strings.TrimPrefix(targetPath, "/")
			content, err := fs.ReadFile(*p.TargetFs, targetPath)
			if err != nil {
				http.Error(w, "File not found", http.StatusNotFound)
				return
			}
			body := strings.ReplaceAll(string(content), "{{.csrfToken}}", csrfToken)
			w.Header().Set("Content-Type", "text/html")
			w.WriteHeader(http.StatusOK)
			w.Write([]byte(body))
			return
		}
		if strings.HasSuffix(r.URL.Path, "/") {
			indexPath := p.TargetFsPrefix + "/" + strings.TrimPrefix(r.URL.Path, "/") + "index.html"
			indexPath = strings.TrimPrefix(indexPath, "/")
			content, err := fs.ReadFile(*p.TargetFs, indexPath)
			if err == nil {
				body := strings.ReplaceAll(string(content), "{{.csrfToken}}", csrfToken)
				w.Header().Set("Content-Type", "text/html")
				w.WriteHeader(http.StatusOK)
				w.Write([]byte(body))
				return
			}
		}
		fsHandler.ServeHTTP(w, r)
	})
}

// Handler returns an http.Handler for the embedded UI.
func (p *PluginUiRouter) Handler() http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		rewrittenURL := strings.TrimPrefix(r.RequestURI, p.HandlerPrefix)
		rewrittenURL = strings.ReplaceAll(rewrittenURL, "//", "/")
		r.URL, _ = url.Parse(rewrittenURL)
		r.RequestURI = rewrittenURL

		subFS, err := fs.Sub(*p.TargetFs, strings.TrimPrefix(p.TargetFsPrefix, "/"))
		if err != nil {
			http.Error(w, "Internal Server Error", http.StatusInternalServerError)
			return
		}

		p.csrfMiddleware(r, http.FileServer(http.FS(subFS))).ServeHTTP(w, r)
	})
}

// RegisterTerminateHandler registers a graceful shutdown endpoint at {prefix}/term.
func (p *PluginUiRouter) RegisterTerminateHandler(termFunc func(), mux *http.ServeMux) {
	p.terminateHandler = termFunc
	if mux == nil {
		mux = http.DefaultServeMux
	}
	mux.HandleFunc(p.HandlerPrefix+"/term", func(w http.ResponseWriter, r *http.Request) {
		p.terminateHandler()
		w.WriteHeader(http.StatusOK)
		go func() {
			time.Sleep(100 * time.Millisecond)
			os.Exit(0)
		}()
	})
}

// HandleFunc registers a handler under the UI path prefix.
func (p *PluginUiRouter) HandleFunc(pattern string, handler http.HandlerFunc, mux *http.ServeMux) {
	if mux == nil {
		mux = http.DefaultServeMux
	}
	if !strings.HasPrefix(pattern, p.HandlerPrefix) {
		pattern = p.HandlerPrefix + pattern
	}
	mux.HandleFunc(pattern, handler)
}

// AttachHandlerToMux attaches the UI file handler to the mux.
func (p *PluginUiRouter) AttachHandlerToMux(mux *http.ServeMux) {
	if mux == nil {
		mux = http.DefaultServeMux
	}
	p.HandlerPrefix = strings.TrimSuffix(p.HandlerPrefix, "/")
	mux.Handle(p.HandlerPrefix+"/", p.Handler())
}
