package router

import (
	"fmt"
	"strings"

	"gordenko.dev/dima/web"
)

type _prefix struct {
	Prefix  string
	Func    web.HandleFunc
	Handler web.Handler
}

type _wrapper struct {
	Prefix  string
	Wrapper web.Wrapper
}

type Router struct {
	routes   map[string]web.HandleFunc
	prefixes []_prefix
	wrappers []_wrapper
	notFound []_prefix
}

func New() *Router {
	s := new(Router)
	s.routes = make(map[string]web.HandleFunc)
	return s
}

func (s *Router) validatePath(path string) error {
	if path == "/" {
		return nil
	}
	if !strings.HasPrefix(path, "/") {
		return fmt.Errorf("Wrong path '%s'. Valid examples: /, /abc", path)
	}
	if strings.HasSuffix(path, "/") {
		return fmt.Errorf("Wrong path '%s'. Valid examples: /, /abc", path)
	}
	return nil
}

func (s *Router) validatePrefix(prefix string) error {
	if prefix == "/" {
		return nil
	}
	if !strings.HasPrefix(prefix, "/") {
		return fmt.Errorf("Wrong prefix '%s'. Valid examples: /, /abc", prefix)
	}
	if strings.HasSuffix(prefix, "/") {
		return fmt.Errorf("Wrong prefix '%s'. Valid examples: /, /abc", prefix)
	}
	return nil
}

func (s *Router) HandleFunc(path string, f web.HandleFunc) {
	if err := s.validatePath(path); err != nil {
		panic(err)
	}
	s.routes[path] = f
}

func (s *Router) Prefix(prefix string, f web.HandleFunc) {
	if err := s.validatePrefix(prefix); err != nil {
		panic(err)
	}
	s.prefixes = append(s.prefixes, _prefix{
		Prefix: prefix,
		Func:   f,
	})
}

func (s *Router) PrefixHandle(prefix string, h web.Handler) {
	if err := s.validatePrefix(prefix); err != nil {
		panic(err)
	}
	s.prefixes = append(s.prefixes, _prefix{
		Prefix:  prefix,
		Handler: h,
	})
}

func (s *Router) NotFound(prefix string, f web.HandleFunc) {
	if err := s.validatePrefix(prefix); err != nil {
		panic(err)
	}
	s.notFound = append(s.notFound, _prefix{
		Prefix: prefix,
		Func:   f,
	})
}

func (s *Router) Wrap(prefix string, w web.Wrapper) {
	if err := s.validatePrefix(prefix); err != nil {
		panic(err)
	}
	s.wrappers = append(s.wrappers, _wrapper{
		Prefix:  prefix,
		Wrapper: w,
	})
}

func (s *Router) Handle(path string, h web.Handler) {
	if err := s.validatePath(path); err != nil {
		panic(err)
	}
	s.routes[path] = h.Handle
}

func (s *Router) Find(path string) (wrappers []web.Wrapper, f web.HandleFunc) {
	f = s.routes[path]
	if f == nil {
		for _, p := range s.prefixes {
			if strings.HasPrefix(path, p.Prefix) {
				if p.Func != nil {
					f = p.Func
				} else {
					f = p.Handler.Handle
				}
				break
			}
		}
	}
	if f == nil {
		for _, p := range s.notFound {
			if strings.HasPrefix(path, p.Prefix) {
				f = p.Func
				break
			}
		}
	}
	if f != nil {
		for _, w := range s.wrappers {
			if strings.HasPrefix(path, w.Prefix) {
				wrappers = append(wrappers, w.Wrapper)
			}
		}
	}
	return
}
