package api

import (
	"box-cost/db/repo"
	"fmt"

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

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

type ArrayCRUDOption struct {
	Collection     string
	ArrayFieldPath string
	NewModel       CreateModel
	EmtyModel      EmptyModel
	DocModel       EmptyModel
	SearchProject  []string
	Query          repo.Map
	OnUpdate       Update
	OnDetail       DetailHandler

	NeedsUpdate bool
	NeedsRemove bool
}

func CreateArrayCRUD(router *GinRouter, prefix string, option *ArrayCRUDOption) {

	//创建
	creatpath := fmt.Sprintf("%s/create/:id", prefix)
	if option.NewModel != nil {

		router.POSTJWT(creatpath, func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
			id := c.Param("id")
			if len(id) < 1 {
				return nil, NewError("参数id不能为空")
			}
			var entity interface{} = nil
			entity, _ = option.NewModel(c, apictx)
			if entity == nil {
				return nil, NewError("数据已存在！")
			}
			ret, _ := repo.RepoDocArrayAppend(apictx.CreateRepoCtx(), option.Collection, id, option.ArrayFieldPath, entity)
			if ret.ModifiedCount == 1 {
				return entity, nil
			}
			return nil, NewError("创建失败!")
		})
	}

	//更新
	if option.NeedsUpdate {
		updatePath := fmt.Sprintf("%s/update/:id", prefix)
		router.POSTJWT(updatePath, func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
			docId := c.Param("id")
			if len(docId) < 1 {
				return nil, NewError("id不能为空!")
			}
			m := option.EmtyModel(c, apictx)
			err := c.ShouldBindJSON(m)
			if err != nil {
				fmt.Println(err)
				return nil, NewError("参数解析错误")
			}

			if option.OnUpdate != nil {
				option.OnUpdate(c, apictx, m)
			}
			updateQuery := repo.Map{}
			for k, v := range option.Query {
				p := fmt.Sprintf("%s.%s", option.ArrayFieldPath, k)
				updateQuery[p] = v
			}
			updateSet := repo.Map{}
			spath := fmt.Sprintf("%s.$", option.ArrayFieldPath)
			updateSet[spath] = m

			updateOption := &repo.ArrayOneUpdateOption{CollectName: option.Collection, Id: docId, Query: updateQuery, Set: updateSet}

			return repo.RepoDocArrayOneUpdate(apictx.CreateRepoCtx(), updateOption)
		})
	}

	//数组所有数据
	SearchPath := fmt.Sprintf("%s/list/:id", prefix)

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

		docId := c.Param("id")
		if len(docId) < 1 {
			return nil, NewError("id不能为空!")
		}
		query := UtilQueryMap(c)
		query["_id"], _ = primitive.ObjectIDFromHex(docId)

		_, list := repo.RepoSeachDocsMap(apictx.CreateRepoCtx(), &repo.DocsSearchOptions{
			CollectName: option.Collection,
			Query:       query,
			Project:     option.SearchProject,
		})

		out := map[string]interface{}{"list": []string{}}
		if list != nil {
			out["list"] = list
		}
		return out, nil
	}
	router.GETJWT(SearchPath, pageSearchFn)

	//删除
	if option.NeedsRemove {
		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("参数不能为空")
			}

			body := map[string]interface{}{}
			c.ShouldBindJSON(&body)

			ArrayQuery := repo.Map{}
			if len(body) < 1 {
				return nil, NewError("body参数不能为空")
			}
			query := bson.M{}
			for k, v := range body {
				query[k] = v
			}
			ArrayQuery[option.ArrayFieldPath] = query

			options := &repo.ArrayOneRemoveOption{
				CollectName: option.Collection,
				Id:          id,
				ArrayQuery:  ArrayQuery,
			}

			ret, err := repo.RepoDocArrayOneRemove(apictx.CreateRepoCtx(), options)
			if ret.ModifiedCount < 1 {
				return nil, NewError("删除失败!")
			}

			return ret, err
		})
	}

	//详情
	if option.OnDetail != nil && option.DocModel != nil {
		DetailPath := fmt.Sprintf("%s/detail/:id", prefix)
		router.GETJWT(DetailPath, func(c *gin.Context, apictx *ApiSession) (interface{}, error) {

			docId := c.Param("id")
			if len(docId) < 1 {
				return nil, NewError("id不能为空!")
			}
			query := UtilQueryMap(c)

			filter := repo.Map{}
			if len(query) > 0 {
				filter[option.ArrayFieldPath] = query
			}

			m := option.DocModel(c, apictx)

			err := repo.RepoDocArraySearch(apictx.CreateRepoCtx(), &repo.ArrayOneSearchOption{
				CollectName: option.Collection,
				ArrayQuery:  filter,
				Id:          docId,
				Field:       option.ArrayFieldPath,
			}, m)

			if err != nil {
				return nil, NewError("数据不存在!")
			}

			retOut, err := option.OnDetail(c, apictx, m)
			if err != nil {
				return nil, err
			}
			if retOut != nil {
				return retOut, nil
			}
			return m, nil
		})
	}

}
