package server

import (
	"context"
	"fmt"
	"net/http"
	"sort"
	"strings"

	handler "git.pyer.club/kingecg/gohttpd/handler"
	"git.pyer.club/kingecg/gohttpd/model"
	logger "git.pyer.club/kingecg/gologger"
	"github.com/nanmu42/gzip"
)

type RequestCtxKey string

type Route struct {
	Method  string
	Path    string
	Handler http.Handler
	matcher *UrlParamMatcher
	middles *MiddlewareLink
}

func (route *Route) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if route.middles.Len() > 0 && !route.middles.ServeHTTP(w, r) {
		return
	}
	route.Handler.ServeHTTP(w, r)
}

func (route *Route) Match(r *http.Request) bool {
	l := logger.GetLogger("Route")
	l.Debug(fmt.Sprintf("match route: %s %s", r.Method, r.URL.Path))
	if route.Method != "" && route.Method != r.Method {
		l.Debug("method not match")
		return false
	}

	if route.matcher != nil && route.matcher.Reg != nil {
		params, matched := MatchUrlParam(r.URL.Path, route.matcher)
		if matched {
			ctx := r.Context()
			data := ctx.Value(RequestCtxKey("data")).(map[string]interface{})
			for _, p := range route.matcher.Params {
				data[p] = params[p]
			}
			return true
		}
		l.Debug("Not match matcher reg")
		return false
	}
	if route.Path == "" {
		return true
	}
	return strings.HasPrefix(r.URL.Path, route.Path)
}

func (route *Route) Add(m Middleware) {
	route.middles.Add(m)
}

// NewRoute 返回一个新的Route实例
// 参数：
// - method: 请求方法
// - path: 请求路径
// - handleFn: http.Handler处理函数
// 返回值：
// - *Route: 一个指向Route的指针
func NewRoute(method string, path string, handleFn http.Handler) *Route {
	ret := &Route{
		Method:  method,
		Path:    path,
		middles: NewMiddlewareLink(),
	}
	p := ParseUrl(path)
	// 使用handleFn构建handler
	ret.Handler = handleFn
	ret.matcher = &p
	return ret
}

type Routes []*Route

func (rs Routes) Less(i, j int) bool {
	iPaths := strings.Split(rs[i].Path, "/")
	jPaths := strings.Split(rs[j].Path, "/")
	if len(iPaths) > len(jPaths) {
		return true
	}
	if len(iPaths) < len(jPaths) {
		return false
	}
	for k := 0; k < len(iPaths); k++ {
		if iPaths[k] != jPaths[k] {
			return iPaths[k] < jPaths[k]
		}
	}
	return false
}

func (rs Routes) Len() int {
	return len(rs)
}

func (rs Routes) Swap(i, j int) {
	rs[i], rs[j] = rs[j], rs[i]
}

// 可以嵌套的Rest http server mux
type RestMux struct {
	Path        string
	routes      Routes
	middlewares *MiddlewareLink
}

func (mux *RestMux) Use(m Middleware) {
	mux.middlewares.Add(m)
}
func (mux *RestMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {

	c := r.Context()
	newRequest := r
	if t := c.Value("data"); t == nil {
		data := map[string]interface{}{}
		cn := context.WithValue(c, RequestCtxKey("data"), data)
		newRequest = r.WithContext(cn)

	}

	if mux.middlewares.Len() > 0 && !mux.middlewares.ServeHTTP(w, newRequest) {
		return
	}

	// 根据r 从routes中找到匹配的路由
	for _, route := range mux.routes {
		if route.Match(newRequest) {
			route.ServeHTTP(w, newRequest)
			return
		}
	}

	http.NotFound(w, r)
}

func (mux *RestMux) HandleFunc(method string, path string, f func(http.ResponseWriter, *http.Request)) *Route {
	r := NewRoute(method, path, http.HandlerFunc(f))
	mux.routes = append(mux.routes, r)
	sort.Sort(mux.routes)
	return r
}

func (mux *RestMux) Get(path string, f func(http.ResponseWriter, *http.Request)) *Route {
	return mux.HandleFunc("GET", path, f)
}

func (mux *RestMux) Post(path string, f func(http.ResponseWriter, *http.Request)) *Route {
	return mux.HandleFunc("POST", path, f)
}

func (mux *RestMux) Put(path string, f func(http.ResponseWriter, *http.Request)) *Route {
	return mux.HandleFunc("PUT", path, f)
}
func (mux *RestMux) Delete(path string, f func(http.ResponseWriter, *http.Request)) *Route {
	return mux.HandleFunc("DELETE", path, f)
}
func (mux *RestMux) Patch(path string, f func(http.ResponseWriter, *http.Request)) *Route {
	return mux.HandleFunc("PATCH", path, f)
}
func (mux *RestMux) Head(path string, f func(http.ResponseWriter, *http.Request)) *Route {
	return mux.HandleFunc("HEAD", path, f)
}

func (mux *RestMux) Option(path string, f func(http.ResponseWriter, *http.Request)) *Route {
	return mux.HandleFunc("OPTION", path, f)
}

func (mux *RestMux) HandleMux(nmux *RestMux) *Route {
	p := nmux.Path
	if !strings.HasSuffix(p, "/") {
		p = p + "/"
	}
	r := NewRoute("", p, nmux)
	mux.routes = append(mux.routes, r)
	sort.Sort(mux.routes)
	return r
}

func NewRestMux(path string) *RestMux {
	ret := &RestMux{
		Path:        path,
		routes:      Routes{},
		middlewares: NewMiddlewareLink(),
	}
	return ret
}

type ServerMux struct {
	http.Handler
	directiveHandlers *MiddlewareLink
	handlers          map[string]http.Handler
	paths             []string
	wrappedHandler    map[string]http.Handler
}

func (s *ServerMux) Handle(pattern string, handler http.Handler) {
	if s.handlers == nil {
		s.handlers = make(map[string]http.Handler)
	}
	s.handlers[pattern] = handler
	s.paths = append(s.paths, pattern)
	// 自定义比较函数排序s.paths
	sort.Slice(s.paths, func(i, j int) bool {
		return len(s.paths[i]) > len(s.paths[j]) || s.paths[i] > s.paths[j]
	})
}

func (s *ServerMux) AddDirective(directiveStr string) {
	l := logger.GetLogger("ServerMux")
	strs := strings.Split(directiveStr, " ")
	directiveName := strs[0]
	params := strs[1:]
	directive, ok := DirectiveMap[directiveName]
	if ok {
		l.Debug(fmt.Sprintf("add directive: %s", directiveName))
		s.directiveHandlers.Add(directive(params...))
	} else {
		l.Error(fmt.Sprintf("directive not found: %s", directiveName))
	}
}

func (s *ServerMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	l := logger.GetLogger("Access")
	data := map[string]interface{}{}
	c := r.Context()
	cn := context.WithValue(c, RequestCtxKey("data"), data)
	newRequest := r.WithContext(cn)
	l.Info(fmt.Sprintf("From %s-%s %s", r.RemoteAddr, r.Method, r.URL.Path))
	for _, p := range s.paths {
		if strings.HasPrefix(r.URL.Path, p) {
			l.Info(fmt.Sprintf("match path: %s", p))
			s.directiveHandlers.ServeHTTP(w, newRequest)
			ctx := newRequest.Context()
			m := ctx.Value(RequestCtxKey("data")).(map[string]interface{})
			_, ok := m["Tg"]
			if ok {
				wrappedHandler := s.wrappedHandler[p]
				if wrappedHandler == nil {
					s.wrappedHandler[p] = gzip.DefaultHandler().WrapHandler(s.handlers[p])
					wrappedHandler = s.wrappedHandler[p]
				}
				wrappedHandler.ServeHTTP(w, newRequest)
			} else {
				s.handlers[p].ServeHTTP(w, newRequest)
			}

			return
		}
	}
	l.Error(fmt.Sprintf("404: %s", r.URL.Path))
	http.NotFound(w, newRequest)
}

func NewServeMux(c *model.HttpServerConfig) *ServerMux {
	l := logger.GetLogger("ServerMux")
	l.Debug("NewServeMux")
	s := &ServerMux{
		directiveHandlers: NewMiddlewareLink(),
		handlers:          make(map[string]http.Handler),
		paths:             []string{},
		wrappedHandler:    make(map[string]http.Handler),
	}

	for _, directive := range c.Directives {
		s.AddDirective(string(directive))
	}
	for _, httpPath := range c.Paths {
		if httpPath.Root != "" {
			fileHandler := handler.NewFileHandler(handler.FileHandler{
				Root:    httpPath.Root,
				Default: httpPath.Default,
			})
			s.Handle(httpPath.Path, fileHandler)
		} else if len(httpPath.Upstreams) != 0 {
			proxyHandler := handler.NewProxyHandler(&httpPath)
			s.Handle(httpPath.Path, proxyHandler)
		} else {
			l.Error("Not supportted server path type :", httpPath.Path)
		}
	}
	return s
}
