package server

import (
	"fmt"
	"io/fs"
	"net/http"

	httputil "git.mstar.dev/mstar/goutils/http"
	"github.com/mstarongithub/passkey"
	"github.com/rs/zerolog/log"

	"git.mstar.dev/mstar/linstrom/storage"
)

type Server struct {
	store  *storage.Storage
	router http.Handler
	server *http.Server
}

func NewServer(
	store *storage.Storage,
	pkey *passkey.Passkey,
	reactiveFS, staticFS fs.FS,
	placeholderFile *string,
) *Server {
	handler := buildRootHandler(pkey, reactiveFS, staticFS, placeholderFile)
	handler = ChainMiddlewares(handler, LoggingMiddleware, ContextValsMiddleware(map[any]any{
		ContextKeyStorage: store,
	}))

	server := http.Server{
		Handler: handler,
	}
	return &Server{
		store:  store,
		router: handler,
		server: &server,
	}
}

func buildRootHandler(
	pkey *passkey.Passkey,
	reactiveFS, staticFS fs.FS,
	placeholderFile *string,
) http.Handler {
	mux := http.NewServeMux()
	mux.Handle(
		"/webauthn/",
		http.StripPrefix(
			"/webauthn",
			forceCorrectPasskeyAuthFlowMiddleware(buildPasskeyAuthRouter(pkey)),
		),
	)
	mux.Handle("/", setupFrontendRouter(reactiveFS, staticFS))
	mux.Handle("/pk/", http.StripPrefix("/pk", http.FileServer(http.Dir("pk-auth"))))
	mux.HandleFunc("/alive", isAliveHandler)
	mux.Handle("/api/", http.StripPrefix("/api", checkSessionMiddleware(setupApiRouter())))

	mux.Handle(
		"/profiling/",
		http.StripPrefix("/profiling", profilingAuthenticationMiddleware(setupProfilingHandler())),
	)
	// temporary until proper route structure exists
	mux.Handle(
		"/authonly/",
		pkey.Auth(
			ContextKeyPasskeyUsername,
			nil,
			func(w http.ResponseWriter, r *http.Request) {
				httputil.HttpErr(
					w,
					HttpErrIdNotAuthenticated,
					"Not authenticated",
					http.StatusUnauthorized,
				)
			},
		)(ChainMiddlewares(setupTestEndpoints(), passkeyIdToAccountIdTransformerMiddleware)),
	)

	mux.HandleFunc("/placeholder-file", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, *placeholderFile)
	})

	return mux
}

func buildPasskeyAuthRouter(pkey *passkey.Passkey) http.Handler {
	router := http.NewServeMux()
	pkey.MountRoutes(router, "/")
	return router
}

func (s *Server) Start(addr string) error {
	log.Info().Str("addr", addr).Msg("Starting server")
	s.server.Addr = addr
	return s.server.ListenAndServe()
}
