package api

import (
	"encoding/hex"
	"fmt"
	"mesh/db/repo"
	"reflect"

	"github.com/gin-gonic/gin"
	"go.mongodb.org/mongo-driver/bson/primitive"
)

type CreateModel func(c *gin.Context, apictx *ApiSession) (interface{}, error)
type EmptyModel func(c *gin.Context, apictx *ApiSession) interface{}

type CreateModel2 func(c *gin.Context, apictx *ApiSession) (interface{}, error)
type SearchFilterFn func(c *gin.Context, apictx *ApiSession, query map[string]interface{}) map[string]interface{}
type Update func(c *gin.Context, apictx *ApiSession, entity interface{})
type SearchPostFn func(page *repo.PageResult, c *gin.Context, apictx *ApiSession, query map[string]interface{}) (interface{}, error)
type RemoveFn func(c *gin.Context, apictx *ApiSession, id string) (interface{}, error)

type CRUDOption struct {
	Collection        string
	NewModel          CreateModel
	EmtyModel         EmptyModel
	SearchFilter      SearchFilterFn
	SearchPostProcess SearchPostFn
	SearchSort        interface{}
	Remove            RemoveFn
	JWT               bool
	SearchProject     []string
	DetailProject     []string
	OnUpdate          Update
	noUpdate          bool
}

func CreateCRUD(router *GinRouter, prefix string, option *CRUDOption) {

	//创建
	if option.NewModel != nil {
		creatpath := fmt.Sprintf("%s/create", prefix)
		router.POSTJWT(creatpath, func(c *gin.Context, apictx *ApiSession) (interface{}, error) {

			entity, err := option.NewModel(c, apictx)
			if err != nil {
				return nil, err
			}

			if entity == nil {
				return nil, NewError("数据已存在！")
			}
			return repo.RepoAddDoc(apictx.CreateRepoCtx(), option.Collection, entity)
		})
	}

	//更新
	if !option.noUpdate {
		updatePath := fmt.Sprintf("%s/update", prefix)
		router.POSTJWT(updatePath, func(c *gin.Context, apictx *ApiSession) (interface{}, error) {

			m := option.EmtyModel(c, apictx)
			err := c.ShouldBindJSON(m)

			if err != nil {
				fmt.Println(err)
				return nil, NewError("参数解析错误")
			}

			v := reflect.ValueOf(m)
			v = v.Elem()

			mid := v.FieldByName("Id")

			if !mid.IsValid() {
				return nil, NewError("ID不能为空")
			}

			slice := mid.Slice(0, mid.Len())
			objId := hex.EncodeToString(slice.Bytes())
			for i := 0; i < 12; i++ {
				mid.Index(i).SetUint(0)
			}

			if option.OnUpdate != nil {
				option.OnUpdate(c, apictx, m)
			}

			out, err := repo.RepoUpdateSetDoc(apictx.CreateRepoCtx(), option.Collection, objId, m)
			if err != nil {
				return nil, err
			}
			if out.MatchedCount != 1 {
				return nil, NewError("文件不存在!")
			}

			return out, nil
		})
	}

	//分页查询
	SearchPath := fmt.Sprintf("%s/list", prefix)

	pageSearchFn := func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
		page, size, query := UtilQueryPageSize(c)

		if option.SearchFilter != nil {
			filter := option.SearchFilter(c, apictx, query)
			for k, v := range filter {
				query[k] = v
			}
		}

		pageOption := &repo.PageSearchOptions{
			CollectName: option.Collection,
			Page:        page,
			Size:        size,
			Query:       query,
			Project:     option.SearchProject,
		}
		if option.SearchSort != nil {
			pageOption.Sort = option.SearchSort
		}

		pageResult, err := repo.RepoPageSearch(apictx.CreateRepoCtx(), pageOption)
		if err != nil {
			return nil, err
		}

		if option.SearchPostProcess != nil {
			return option.SearchPostProcess(pageResult, c, apictx, query)
		}

		return pageResult, nil
	}

	if option.JWT {
		router.GETJWT(SearchPath, pageSearchFn)
	} else {
		router.GET(SearchPath, pageSearchFn)
	}
	//删除
	removePath := fmt.Sprintf("%s/delete/:id", prefix)
	router.POSTJWT(removePath, func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
		id := c.Param("id")
		if len(id) < 1 {
			return nil, NewError("参数不能为空")
		}

		if option.Remove != nil {
			return option.Remove(c, apictx, id)
		}

		return repo.RepoDeleteDoc(apictx.CreateRepoCtx(), option.Collection, id)

	})

	//详情
	if len(option.DetailProject) > 0 {
		detailPath := fmt.Sprintf("%s/detail/:id", prefix)
		router.GET(detailPath, func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
			id := c.Param("id")
			if len(id) < 1 {
				return nil, NewError("参数不能为空")
			}
			uid, _ := primitive.ObjectIDFromHex(id)

			ok, ret := repo.RepoSeachDocMap(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
				CollectName: option.Collection,
				Query:       repo.Map{"_id": uid},
				Project:     option.DetailProject,
			})

			if !ok {
				return nil, NewError("没有查询到数据")
			}
			return ret, nil
		})
	}
}
