package discord_router

import "github.com/bwmarrin/discordgo"

type HandlerFunc func(*discordgo.Session, *discordgo.InteractionCreate)

type RouteEntry struct {
	CommandName string
	Handler     HandlerFunc
}

func (re *RouteEntry) Match(i *discordgo.InteractionCreate) bool {
	switch i.Type {
	case discordgo.InteractionApplicationCommand:
		if i.ApplicationCommandData().Name != re.CommandName {
			return false
		}
	case discordgo.InteractionMessageComponent:
		if i.MessageComponentData().CustomID != re.CommandName {
			return false
		}
	}

	return true
}

type Router struct {
	session *discordgo.Session
	routes  []RouteEntry
	group   []Group
}

func NewApp(s *discordgo.Session) *Router {
	return &Router{
		session: s,
	}
}

func (r *Router) Route(cmd string, handlerFunc HandlerFunc) *Router {

	r.routes = append(r.routes, RouteEntry{
		CommandName: cmd,
		Handler:     handlerFunc,
	})

	return r
}

func (r *Router) Serve(s *discordgo.Session, i *discordgo.InteractionCreate) {
	for _, e := range r.routes {
		ok := e.Match(i)
		if ok {
			e.Handler(s, i)
		}
	}

	for _, g := range r.group {
		for _, e := range g.routes {
			ok := e.Match(i)
			if ok {
				if len(g.middleware) < 1 {
					e.Handler(s, i)
				}

				wrapped := e.Handler

				// loop in reverse to preserve middleware order
				for i := len(g.middleware) - 1; i >= 0; i-- {
					wrapped = g.middleware[i](wrapped)
				}
				wrapped(s, i)
			}
		}
	}
}

type Middleware func(HandlerFunc) HandlerFunc

type Group struct {
	routes     []RouteEntry
	middleware []Middleware
}

func (r *Router) Use(m ...Middleware) *Group {

	r.group = append(r.group, Group{
		routes:     []RouteEntry{},
		middleware: m,
	})

	return &r.group[len(r.group)-1]
}

func (g *Group) Route(cmd string, handlerFunc HandlerFunc) *Group {

	g.routes = append(g.routes, RouteEntry{
		CommandName: cmd,
		Handler:     handlerFunc,
	})
	return g
}
