package lion

import (
	"net/http"
	"reflect"
	"strings"
)

// Resource defines the minimum required methods
type Resource interface{}

// resourceUses is an interface with the Uses() method which can be used to define global middlewares for the resource.
type resourceUses interface {
	Uses() Middlewares
}

// Resource registers a Resource with the corresponding pattern
func (r *Router) Resource(pattern string, resource Resource) {
	sub := r.Group(pattern)

	if usesRes, ok := resource.(resourceUses); ok {
		if len(usesRes.Uses()) > 0 {
			sub.Use(usesRes.Uses()...)
		}
	}

	for _, m := range allowedHTTPMethods {
		if hfn, ok := isHandlerFuncInResource(m, resource); ok {
			s := sub.Subrouter()
			if mws, ok := isMiddlewareInResource(m, resource); ok {
				s.Use(mws()...)
			}
			s.HandleFunc(m, "/", http.HandlerFunc(hfn))
		}
	}
}

// checks if there is a Name(w http.ResponseWriter, r *http.Request) method available on the Resource r
func isHandlerFuncInResource(m string, r Resource) (func(w http.ResponseWriter, r *http.Request), bool) {
	name := strings.Title(strings.ToLower(m))
	method := reflect.ValueOf(r).MethodByName(name)
	if !method.IsValid() {
		return nil, false
	}

	// Native http.HandlerFunc
	fn, ok := method.Interface().(func(w http.ResponseWriter, r *http.Request))
	if ok {
		return fn, true
	}

	// ... or check for a contextual handler
	cfn, ok := method.Interface().(func(Context))
	if !ok {
		return nil, false
	}
	return wrap(cfn).ServeHTTP, ok
}

// checks if there is a NameMiddlewares() Middlewares method available on the Resource r
func isMiddlewareInResource(m string, r Resource) (func() Middlewares, bool) {
	name := strings.Title(strings.ToLower(m)) + "Middlewares"
	method := reflect.ValueOf(r).MethodByName(name)
	if !method.IsValid() {
		return nil, false
	}

	fn, ok := method.Interface().(func() Middlewares)
	return fn, ok
}
