package routing

import (
	"git.markbailey.dev/cerbervs/ptpp/app/controller"
	"git.markbailey.dev/cerbervs/ptpp/app/handler"
	"git.markbailey.dev/cerbervs/ptpp/app/session"
	"git.markbailey.dev/cerbervs/ptpp/lib/database"
	werror "git.markbailey.dev/cerbervs/ptpp/lib/error"
	"git.markbailey.dev/cerbervs/ptpp/lib/logger"
	"git.markbailey.dev/cerbervs/ptpp/lib/middleware"
	"git.markbailey.dev/cerbervs/ptpp/util"
	"log"
	"net/http"
)

var sess session.IManager

type IRouter interface {
	RegisterRoutes() http.Handler
}

type Route struct {
	Controller controller.IController
	Path       string
	Name       string
}

type Router struct {
	Mux        *http.ServeMux
	BasePath   string
	Routes     []Route
	SubRouters *[]Router
	Middleware *[]middleware.Func
}

func (r Router) HandleAllRequestMethods(route Route) {
	r.Mux.Handle("GET "+r.BasePath+route.Path, handler.Handler(route.Controller.Get))
	r.Mux.Handle("OPTIONS "+r.BasePath+route.Path, handler.Handler(route.Controller.Options))
	r.Mux.Handle("TRACE "+r.BasePath+route.Path, handler.Handler(route.Controller.Trace))
	r.Mux.Handle("PUT "+r.BasePath+route.Path, handler.Handler(route.Controller.Put))
	r.Mux.Handle("DELETE "+r.BasePath+route.Path, handler.Handler(route.Controller.Delete))
	r.Mux.Handle("POST "+r.BasePath+route.Path, handler.Handler(route.Controller.Post))
	r.Mux.Handle("PATCH "+r.BasePath+route.Path, handler.Handler(route.Controller.Patch))
	r.Mux.Handle("CONNECT "+r.BasePath+route.Path, handler.Handler(route.Controller.Connect))
}

func (r Router) RegisterRoutes() http.Handler {
	if r.Mux == nil {
		r.Mux = http.NewServeMux()
	}

	for _, route := range r.Routes {
		if err := route.Controller.Init(sess, database.ChooseDB(), logger.NewCompositeLogger()); err != nil {
			panic(err)
		}

		r.HandleAllRequestMethods(route)
	}

	if r.SubRouters != nil {
		for _, subRouter := range *r.SubRouters {
			sr := subRouter.RegisterRoutes()
			r.Mux.Handle("GET "+r.BasePath+subRouter.BasePath, sr)
		}
	}

	if r.Middleware != nil {
		mw := middleware.Compose(*r.Middleware)
		return mw(r.Mux)
	}

	return r.Mux
}

func (r Router) RegisterFs() {
	if r.Mux == nil {
		r.Mux = http.NewServeMux()
	}

	fs := http.FileServer(http.Dir(util.GetFullyQualifiedPath("/public")))
	log.Println("Serving static files from: " + util.GetFullyQualifiedPath("/public"))
	r.Mux.Handle("GET "+r.BasePath+"public/", http.StripPrefix("/public/", fs))
}

type RouteMapping struct {
	Path string
	Name string
}

func (r Router) GetFlatRouteList() []RouteMapping {
	var routes []RouteMapping

	for _, route := range r.Routes {
		routes = append(routes, RouteMapping{Path: route.Path, Name: route.Name})
	}

	for _, subRouter := range *r.SubRouters {
		routes = append(routes, subRouter.GetFlatRouteList()...)
	}

	return routes
}

func (r Router) GetRouteByName(name string) (string, error) {
	for _, route := range r.GetFlatRouteList() {
		if route.Name == name {
			return route.Path, nil
		}
	}

	return "", werror.Wrap(nil, "Route not found")
}
func init() {
	var err error

	sess, err = session.NewManager("memory", "ptpp", 3600)
	if err != nil {
		panic(werror.Wrap(err, "Error creating session manager"))
	}
}
