package ginutils

import (
	"encoding/json"
	"net/http"

	"github.com/gin-gonic/gin"
)

// so ugly for conflict in golang lint and gin standard error handle.
func ReturnError(ctx *gin.Context, err error) {
	ctx.Error(err) // nolint: errcheck
}

type HandlerFuncResult interface{}

type HandlerFunc func(c *gin.Context) (HandlerFuncResult, error)

type Route gin.IRoutes

type MyRoute struct {
	Route

	DisableFilterByQueryMiddleWare bool
}

type WrapHandler struct {
	handlerFunc HandlerFunc

	DisableFilterByQueryMiddleWare bool
}

func (handler *WrapHandler) Handle(c *gin.Context) {
	res, err := handler.handlerFunc(c)
	if err != nil {
		ReturnError(c, err)
		return
	}
	if !handler.DisableFilterByQueryMiddleWare {
		res = FilterByQueryMiddlwWare(c, res)
	}

	c.JSON(http.StatusOK, res)
}

func (r MyRoute) HandleWithError(method string, path string, handlerFunc HandlerFunc) {
	r.Handle(method, path, (&WrapHandler{handlerFunc: handlerFunc, DisableFilterByQueryMiddleWare: r.DisableFilterByQueryMiddleWare}).Handle)
}

type FilterResult map[string]json.RawMessage

func FilterByQuery(results []FilterResult, filters map[string]string) []FilterResult {
	res := []FilterResult{}
	for _, result := range results {
		filtered := false
		for filter, value := range filters {
			valueStr := string(result[filter])
			_ = json.Unmarshal(result[filter], &valueStr)

			if valueStr != value {
				filtered = true
				continue
			}
		}
		if !filtered {
			res = append(res, result)
		}
	}
	return res
}

var (
	QueryWhiteList = []string{"orderby", "sortby"}
)

func FilterByQueryMiddlwWare(c *gin.Context, res HandlerFuncResult) HandlerFuncResult {
	query := c.Request.URL.Query()
	filter := map[string]string{}
	for k, v := range query {
		if len(v) != 1 || v[0] == "" {
			continue
		}
		filter[k] = v[0]
	}

	if len(query) == 0 {
		return res
	}

	bytes, err := json.Marshal(res)
	if err != nil {
		return res
	}

	filterListRes := []FilterResult{}

	err = json.Unmarshal(bytes, &filterListRes)
	if err != nil {
		return res
	}

	result := FilterByQuery(filterListRes, filter)
	return result
}
