package bootable

import (
	"git.simanc.com/software/golib/v4/features/mo"
	"git.simanc.com/software/golib/v4/infra/ii"
)

type Response struct {
	Rows  []mo.M `json:"rows"`
	Total int64  `json:"total"`
	Ret   string `json:"ret"`
}

// Filter 查询参数
type Filter struct {
	Limit   int64  `json:"limit,omitempty"`
	Offset  int64  `json:"offset,omitempty"`
	ExtName string `json:"name,omitempty"`   // ExtName 用于 Search
	Search  string `json:"search,omitempty"` // Search 用于 Toolbar search
	Sort    string `json:"sort,omitempty"`   // Field ID
	Order   string `json:"order,omitempty"`  // ASC/DESC
	Filter  string `json:"filter,omitempty"` // Filter 用于 filter control
	Custom  mo.D   `bson:"custom,omitempty"` // Custom 自定义查询条件, 使用 bson, 支持 MongoDB json 查询语法

	user ii.User
}

const (
	OrderASC  = "asc"
	OrderDESC = "desc"
)

// Build 解析查询参数, 当 Search 和 Filter 同时存在时, Filter 生效
// 该方法需要设置为 ajax/post
func (q *Filter) Build(info *ii.ItemInfo, items ii.Items) (mo.Pipeline, error) {
	p := mo.Pipeline{}
	if q.Order != "" {
		p = append(p, q.ParseSorter())
	}

	matcher := mo.Matcher{}
	// 请求查询条件
	if len(q.Custom) > 0 {
		q.handleParams(info, items, &matcher, q.Custom, true)
	}

	// filter 查询条件
	// 将 json 字符串使用轻松模式解析为 mo.D 以便保持 json 结构字段顺序
	var doc mo.D
	if q.Filter != "" {
		if err := mo.UnmarshalExtJSON([]byte(q.Filter), false, &doc); err != nil {
			return nil, err
		}
	} else if q.Search != "" {
		doc = append(doc, mo.E{Key: q.ExtName, Value: q.Search})
	}

	q.handleParams(info, items, &matcher, doc, false)

	if done := matcher.Done(); len(done) > 0 {
		p = append(p, matcher.Pipeline())
	}

	arg, err := info.Aggregation(items)
	if err != nil {
		return nil, err
	}
	if len(arg) > 0 {
		p = append(p, arg...)
	}

	if q.Offset > 0 {
		p = append(p, mo.NewSkip(q.Offset).Pipeline())
	}

	if q.Limit > 0 {
		p = append(p, mo.NewLimiter(q.Limit).Pipeline())
	}

	return p, nil
}

func (q *Filter) ParseSorter() mo.D {
	switch q.Order {
	case OrderASC:
		return (&mo.Sorter{}).AddASC(q.Sort).Pipeline()
	case OrderDESC:
		return (&mo.Sorter{}).AddDESC(q.Sort).Pipeline()
	default:
		return nil
	}
}
