package port

import (
	"net/http"
	"sync"

	"gitea.dwysokinski.me/twhelp/corev3/internal/app"
	"gitea.dwysokinski.me/twhelp/corev3/internal/port/internal/apimodel"
	"gitea.dwysokinski.me/twhelp/corev3/internal/port/internal/swgui"
	"github.com/getkin/kin-openapi/openapi3"
	"github.com/go-chi/chi/v5"
	"github.com/go-chi/chi/v5/middleware"
)

type apiHTTPHandler struct {
	versionSvc    *app.VersionService
	serverSvc     *app.ServerService
	tribeSvc      *app.TribeService
	playerSvc     *app.PlayerService
	errorRenderer apiErrorRenderer
	openAPISchema func() (*openapi3.T, error)
}

func WithOpenAPIConfig(oapiCfg OpenAPIConfig) APIHTTPHandlerOption {
	return func(cfg *apiHTTPHandlerConfig) {
		cfg.openAPI = oapiCfg
	}
}

func NewAPIHTTPHandler(
	versionSvc *app.VersionService,
	serverSvc *app.ServerService,
	tribeSvc *app.TribeService,
	playerSvc *app.PlayerService,
	opts ...APIHTTPHandlerOption,
) http.Handler {
	cfg := newAPIHTTPHandlerConfig(opts...)

	h := &apiHTTPHandler{
		versionSvc: versionSvc,
		serverSvc:  serverSvc,
		tribeSvc:   tribeSvc,
		playerSvc:  playerSvc,
		openAPISchema: sync.OnceValues(func() (*openapi3.T, error) {
			return getOpenAPISchema(cfg.openAPI)
		}),
	}

	r := chi.NewRouter()

	if cfg.openAPI.Enabled {
		r.Group(func(r chi.Router) {
			if cfg.openAPI.SwaggerEnabled {
				r.HandleFunc("/v2/swagger", func(w http.ResponseWriter, r *http.Request) {
					http.Redirect(w, r, r.RequestURI+"/", http.StatusMovedPermanently)
				})
				r.Handle("/v2/swagger/*", swgui.Handler(
					cfg.openAPI.BasePath+"/v2/swagger",
					cfg.openAPI.BasePath+"/v2/openapi3.json",
				))
			}
			r.Get("/v2/openapi3.json", h.sendOpenAPIJSON)
		})
	}

	// these handlers must be set after the swagger/openapi routes
	// because we don't want to override the default handlers for those routes
	r.NotFound(h.handleNotFound)
	r.MethodNotAllowed(h.handleMethodNotAllowed)

	return apimodel.HandlerWithOptions(h, apimodel.ChiServerOptions{
		BaseRouter: r,
		Middlewares: []apimodel.MiddlewareFunc{
			middleware.NoCache,
			h.versionMiddleware,
			h.serverMiddleware,
			h.tribeMiddleware,
			h.playerMiddleware,
		},
		ErrorHandlerFunc: func(w http.ResponseWriter, r *http.Request, err error) {
			h.errorRenderer.render(w, r, err)
		},
	})
}

func (h *apiHTTPHandler) handleNotFound(w http.ResponseWriter, r *http.Request) {
	h.errorRenderer.render(w, r, apiError{
		status:  http.StatusNotFound,
		code:    "route-not-found",
		message: "route not found",
	})
}

func (h *apiHTTPHandler) handleMethodNotAllowed(w http.ResponseWriter, r *http.Request) {
	h.errorRenderer.render(w, r, apiError{
		status:  http.StatusMethodNotAllowed,
		code:    "method-not-allowed",
		message: "method not allowed",
	})
}
