package mo

import (
	"reflect"
	"slices"
	"strings"
)

type PipeCollection interface {
	Pipeline() D
}

type Filter interface {
	Done() D
}

// Grouper
// Group 拥有 100MB 内存大小限制 https://www.mongodb.com/docs/v6.0/reference/operator/aggregation/group/#-group-and-memory-restrictions
type Grouper struct {
	Filter D
}

func (g *Grouper) Add(k string, v any) *Grouper {
	g.Filter = append(g.Filter, E{Key: k, Value: v})
	return g
}

func (g *Grouper) Done() D {
	if g.Filter == nil {
		return D{}
	}
	return g.Filter
}

func (g *Grouper) Pipeline() D {
	return D{{Key: ArgGroup, Value: g.Filter}}
}

func (g *Grouper) UnmarshalJSON(v []byte) error {
	return UnmarshalExtJSON(v, true, g.Pipeline())
}

func (g *Grouper) MarshalJSON() ([]byte, error) {
	return MarshalExtJSON(g.Pipeline(), true, true)
}

// Matcher 匹配编译器
// 注意: MongoDB 根据传入指令的顺序进行查询
type Matcher struct {
	Filter D
}

// Add 添加查询条件, 当已存在的方法不满足查询条件时, 可用此方法添加原始查询
// db.inventory.find( { size: { h: 14, w: 21, uom: "cm" } } )
func (m *Matcher) Add(k string, v any) *Matcher {
	m.Filter = append(m.Filter, E{Key: k, Value: v})
	return m
}

// Delete 根据 k 删除已添加的条件然后返回其值, 请勿过于依赖此方法
func (m *Matcher) Delete(k string) any {
	for i, v := range m.Filter {
		if v.Key == k {
			m.Filter = slices.Delete(m.Filter, i, i+1)
			return v.Value
		}
	}
	return nil
}

// Replace 替换已存在的条件
func (m *Matcher) Replace(filter D) *Matcher {
	m.Filter = filter
	return m
}

// In 数组
// db.inventory.find( { status: { $in: [ "A", "D" ] } } )
func (m *Matcher) In(k string, v A) *Matcher {
	m.Add(k, D{{Key: "$in", Value: v}})
	return m
}

// Nin 数组反选
// { field: { $nin: [ <value1>, <value2> ... <valueN> ] } }
// // https://www.mongodb.com/docs/v6.0/reference/operator/query/nin/
func (m *Matcher) Nin(k string, v A) *Matcher {
	m.Add(k, D{{Key: "$nin", Value: v}})
	return m
}

// Eq 相等
func (m *Matcher) Eq(k string, v any) *Matcher {
	if reflect.TypeOf(v).Kind() == reflect.Struct || reflect.TypeOf(v).Kind() == reflect.Map {
		return m.EqEachMap(k, v)
	}
	m.Add(k, D{{Key: "$eq", Value: v}})
	return m
}

// EqEachMap 等同于 Eq 但是会将 v 作为子 map 展开添加到查询
func (m *Matcher) EqEachMap(k string, v any) *Matcher {
	var filter M
	if val, ok := v.(M); ok {
		filter = val
	}
	if err := Decode(v, &filter); err == nil {
		for sk, sv := range filter {
			m.Eq(k+"."+sk, sv)
		}
	}
	return m
}

func (m *Matcher) CloneMust() *Matcher {
	b, err := Marshal(m.Filter)
	if err != nil {
		panic(err)
	}
	var filter D
	if err = Unmarshal(b, &filter); err != nil {
		panic(err)
	}
	return &Matcher{Filter: filter}
}

// Ne 不相等
// { field: { $ne: value } }
// // https://www.mongodb.com/docs/v6.0/reference/operator/query/ne/
func (m *Matcher) Ne(k string, v any) *Matcher {
	m.Add(k, D{{Key: "$ne", Value: v}})
	return m
}

// Gt 大于
func (m *Matcher) Gt(k string, v any) *Matcher {
	m.Add(k, D{{Key: "$gt", Value: v}})
	return m
}

// Gte 大于等于
func (m *Matcher) Gte(k string, v any) *Matcher {
	m.Add(k, D{{Key: "$gte", Value: v}})
	return m
}

// Lt 小于
// Lt db.inventory.find( { status: "A", qty: { $lt: 30 } } )
func (m *Matcher) Lt(k string, v any) *Matcher {
	m.Add(k, D{{Key: "$lt", Value: v}})
	return m
}

// Lte 小于等于
func (m *Matcher) Lte(k string, v any) *Matcher {
	m.Add(k, D{{Key: "$lte", Value: v}})
	return m
}

// All 等效于 And 对指定值的操作；即以下声明：
// { tags: { $all: [ "ssl" , "security" ] } }
// { $and: [ { tags: "ssl" }, { tags: "security" } ] }
func (m *Matcher) All(k string, v A) *Matcher {
	m.Add(k, D{{Key: "$all", Value: v}})
	return m
}

// Regex 正则表达式 https://www.mongodb.com/docs/v6.0/reference/operator/query/regex/
// db.products.find( { description: { $regex: /^S/, $options: 'm' } } )
// opt 为操作符:
// i 区分大小写
// m https://www.mongodb.com/docs/v6.0/reference/operator/query/regex/#multiline-match-for-lines-starting-with-specified-pattern
// x
// s 允许匹配点 (.) 字符
// 操作符可以连用
// 正则表达式操作符 https://www.mongodb.com/docs/v6.0/reference/operator/query/regex/#mongodb-query-op.-options
func (m *Matcher) Regex(k string, v any, opt ...string) *Matcher {
	val := D{{Key: "$regex", Value: v}}
	if len(opt) > 0 {
		val = append(val, E{Key: "$options", Value: strings.Join(opt, "")})
	}
	m.Add(k, val)
	return m
}

// Not 等于 Regex 正则表达式的反选
// db.inventory.find( { price: { $not: { $gt: 1.99 } } } )
// db.inventory.find( { item: { $not: /^p.*/ } } )
// db.inventory.find( { item: { $not: { $regex: "^p.*" } } } )
// TODO Not 指令似乎仅支持一个 Key/Val, 待验证
func (m *Matcher) Not(k string, v any) *Matcher {
	m.Add(k, D{{Key: "$not", Value: v}})
	return m
}

// Or 或者
// db.inventory.find( { $or: [ { status: "A" }, { qty: { $lt: 30 } } ] } )
// https://www.mongodb.com/docs/v6.0/reference/operator/query/or/
func (m *Matcher) Or(v *Matcher) *Matcher {
	m.Add("$or", m.toSlice(v))
	return m
}

// And 所有条件相等时
// { $and: [ { tags: "ssl" }, { tags: "security" } ] }
// https://www.mongodb.com/docs/v6.0/reference/operator/query/and/
func (m *Matcher) And(v *Matcher) *Matcher {
	m.Add("$and", m.toSlice(v))
	return m
}

// Nor
// db.inventory.find( { $nor: [ { price: 1.99 }, { sale: true } ]  } )
// db.inventory.find( { $nor: [ { price: 1.99 }, { price: { $exists: false } }, { sale: true }, { sale: { $exists: false } } ] } )
// https://www.mongodb.com/docs/v6.0/reference/operator/query/nor/
func (m *Matcher) Nor(v *Matcher) *Matcher {
	m.Add("$nor", m.toSlice(v))
	return m
}

// ElemMatch 数组元素查找, elemMatch 会匹配数组内的所有元素
// 数字类型: db.scores.find( { results: { $elemMatch: { $gte: 80, $lt: 85 } } } )
// Object 类型:
// db.survey.find( { results: { $elemMatch: { product: "xyz", score: { $gte: 8 } } } } )
// db.survey.find( { results: { $elemMatch: { product: "xyz" } } } )
// https://www.mongodb.com/docs/manual/reference/operator/query/elemMatch/
func (m *Matcher) ElemMatch(field string, v *Matcher) *Matcher {
	for i, ele := range m.Filter {
		if ele.Key == field {
			m.Filter[i] = E{Key: "$elemMatch", Value: append(ele.Value.(D), v.Done()...)}
			return m
		}
	}
	m.Add(field, D{{Key: "$elemMatch", Value: v.Done()}})
	return m
}

func (m *Matcher) toSlice(v *Matcher) A {
	filter := v.Done()
	builder := make(A, len(filter))
	for i, e := range filter {
		builder[i] = D{e}
	}
	return builder
}

func (m *Matcher) Done() D {
	if m.Filter == nil {
		return D{}
	}
	return m.Filter
}

func (m *Matcher) Pipeline() D {
	return D{{Key: ArgMatch, Value: m.Filter}}
}

func (m *Matcher) UnmarshalJSON(v []byte) error {
	return UnmarshalExtJSON(v, true, m.Pipeline())
}

func (m *Matcher) MarshalJSON() ([]byte, error) {
	return MarshalExtJSON(m.Pipeline(), true, true)
}

func (m *Matcher) String() string {
	b, err := m.MarshalJSON()
	if err != nil {
		return err.Error()
	}
	return string(b)
}

// Projects 控制返回的字段
type Projects struct {
	Filter D
}

// AddEnable Value 为非 0 的数时表示返回此字段
func (p *Projects) AddEnable(k string) *Projects {
	p.Filter = append(p.Filter, E{Key: k, Value: 1})
	return p
}

// AddDisable Value 为 0 时表示不返回此字段
func (p *Projects) AddDisable(k string) *Projects {
	p.Filter = append(p.Filter, E{Key: k, Value: 0})
	return p
}

func (p *Projects) Done() D {
	if p.Filter == nil {
		return D{}
	}
	return p.Filter
}

func (p *Projects) Pipeline() D {
	return D{{Key: ArgProject, Value: p.Filter}}
}

func (p *Projects) UnmarshalJSON(v []byte) error {
	return UnmarshalExtJSON(v, true, p.Pipeline())
}

func (p *Projects) MarshalJSON() ([]byte, error) {
	return MarshalExtJSON(p.Pipeline(), true, true)
}

// Sorter
// Sort 根据字段对文档排序, 最多可以指定 32 个字段 https://www.mongodb.com/docs/v6.0/reference/operator/aggregation/sort/
type Sorter struct {
	Filter D
}

const (
	SortASC  = int64(1)
	SortDESC = int64(-1)
)

func (s *Sorter) AddASC(k string) *Sorter {
	if s.inLimited() {
		return s
	}
	s.Filter = append(s.Filter, E{Key: k, Value: SortASC})
	return s
}

func (s *Sorter) AddDESC(k string) *Sorter {
	if s.inLimited() {
		return s
	}
	s.Filter = append(s.Filter, E{Key: k, Value: SortDESC})
	return s
}

func (s *Sorter) Done() D {
	if s.Filter == nil {
		return D{}
	}
	return s.Filter
}

func (s *Sorter) Pipeline() D {
	return D{{Key: ArgSort, Value: s.Filter}}
}

func (s *Sorter) UnmarshalJSON(v []byte) error {
	return UnmarshalExtJSON(v, true, s.Pipeline())
}

func (s *Sorter) MarshalJSON() ([]byte, error) {
	return MarshalExtJSON(s.Pipeline(), true, true)
}

func (s *Sorter) inLimited() bool {
	return len(s.Filter) > 32
}

func NewSorter(k string, sort int64) *Sorter {
	s := &Sorter{}
	s.Filter = append(s.Filter, E{Key: k, Value: sort})
	return s
}

type Limiter struct {
	Limit int64
}

func (l *Limiter) Pipeline() D {
	return D{{Key: ArgLimit, Value: l.Limit}}
}

func (l *Limiter) UnmarshalJSON(v []byte) error {
	return UnmarshalExtJSON(v, true, l.Pipeline())
}

func (l *Limiter) MarshalJSON() ([]byte, error) {
	return MarshalExtJSON(l.Pipeline(), true, true)
}

func NewLimiter(limit int64) *Limiter {
	if limit < 0 {
		limit = 0
	}
	return &Limiter{Limit: limit}
}

type Skipper struct {
	Skip int64
}

func (s *Skipper) Pipeline() D {
	return D{{Key: ArgSkip, Value: s.Skip}}
}

func (s *Skipper) UnmarshalJSON(v []byte) error {
	return UnmarshalExtJSON(v, true, s.Pipeline())
}

func (s *Skipper) MarshalJSON() ([]byte, error) {
	return MarshalExtJSON(s.Pipeline(), true, true)
}

func NewSkip(skip int64) *Skipper {
	if skip < 0 {
		skip = 0
	}
	return &Skipper{Skip: skip}
}

type Looker struct {
	From         string
	LocalField   string
	ForeignField string
	Let          D
	Pipe         Pipeline
	As           string
}

func (l *Looker) SetFrom(from string) *Looker {
	l.From = from
	return l
}

func (l *Looker) SetLocalField(field string) *Looker {
	l.LocalField = field
	return l
}

func (l *Looker) SetForeignField(filed string) *Looker {
	l.ForeignField = filed
	return l
}

func (l *Looker) SetLet(let D) *Looker {
	l.Let = let
	return l
}

func (l *Looker) SetPipe(pipe Pipeline) *Looker {
	l.Pipe = pipe
	return l
}

func (l *Looker) SetAs(as string) *Looker {
	l.As = as
	return l
}

func (l *Looker) Pipeline() D {
	m := D{}
	if l.From != "" {
		m = append(m, E{Key: "from", Value: l.From})
	}
	if l.LocalField != "" {
		m = append(m, E{Key: "localField", Value: l.LocalField})
	}
	if l.ForeignField != "" {
		m = append(m, E{Key: "foreignField", Value: l.ForeignField})
	}
	if len(l.Let) > 0 {
		m = append(m, E{Key: "let", Value: l.Let})
	}
	if len(l.Pipe) > 0 {
		m = append(m, E{Key: "pipeline", Value: l.Pipe})
	}
	if l.As != "" {
		m = append(m, E{Key: "as", Value: l.As})
	}
	return D{{Key: ArgLookup, Value: m}}
}

func (l *Looker) UnmarshalJSON(v []byte) error {
	return UnmarshalExtJSON(v, true, l.Pipeline())
}

func (l *Looker) MarshalJSON() ([]byte, error) {
	return MarshalExtJSON(l.Pipeline(), true, true)
}

// Setter 使用 $addField/$set 为查询的结果新增字段, 其中 $set 为 $addField 的别名
// 添加的 值 可使用管道表达式 https://www.mongodb.com/docs/manual/meta/aggregation-quick-reference/#std-label-aggregation-expressions
type Setter struct {
	Filter D
}

func (s *Setter) Add(field string, filter D) {
	s.Filter = append(s.Filter, E{Key: field, Value: filter})
}

// SUM 合计字段的值, 当字段重复出现时则会重复计算
// 联合 $add 运算符一起使用
func (s *Setter) SUM(fieldName string, field []string) {
	for i := 0; i < len(field); i++ {
		if strings.HasPrefix(field[i], "$") {
			continue
		}
		field[i] = "$" + field[i]
	}
	s.Add(fieldName, D{{Key: OptSum, Value: D{{Key: OptAdd, Value: field}}}})
}

func (s *Setter) Pipeline() D {
	return D{{Key: ArgSet, Value: s.Filter}}
}

// Piper Add New in MongoDB 6.0
type Piper struct {
	pipe Pipeline
}

func (p *Piper) Match(matcher PipeCollection) {
	p.pipe = append(p.pipe, matcher.Pipeline())
}

func (p *Piper) Lookup(looker PipeCollection) {
	p.pipe = append(p.pipe, looker.Pipeline())
}

// Documents 搜索文档
// https://www.mongodb.com/docs/v6.0/reference/operator/aggregation/documents/#examples
func (p *Piper) Documents(d D) {
	p.pipe = append(p.pipe, D{{Key: ArgDocuments, Value: d}})
}

func (p *Piper) Pipeline() Pipeline {
	return p.pipe
}

type Updater struct {
	Setter      D
	UnSetter    D
	Pusher      D
	Puller      D
	PullerAll   D
	CurDate     D
	SetOnInsert D
}

// Set 将 k 字段的内容更新为 v
// Set 不适用于更新数据类型为 TypeArray 的字段, 请使用 Push 或 Pull 系列方法
// https://www.mongodb.com/docs/manual/reference/operator/update/set/
func (o *Updater) Set(k string, v any) {
	o.SetE(E{Key: k, Value: v})
}

func (o *Updater) SetE(e E) {
	o.Setter = append(o.Setter, e)
}

// Unset 从文档中删除此字段
// https://www.mongodb.com/docs/manual/reference/operator/update/unset/
func (o *Updater) Unset(k string) {
	o.UnSetter = append(o.UnSetter, E{Key: k, Value: nil})
}

// Push 将 v 添加到数组的最后面
// https://www.mongodb.com/docs/manual/reference/operator/update/push/
func (o *Updater) Push(k string, v any) {
	o.Pusher = append(o.Pusher, E{Key: k, Value: v})
}

// PushEach 将 v 的展开并添加到数组的后面
// https://www.mongodb.com/docs/manual/reference/operator/update/each/
func (o *Updater) PushEach(k string, v A) {
	o.Pusher = append(o.Pusher, E{Key: k, Value: D{{Key: "$each", Value: v}}})
}

// Pull 删除数组内的元素 v
// https://www.mongodb.com/docs/manual/reference/operator/update/pull/
func (o *Updater) Pull(k string, v any) {
	o.Puller = append(o.Puller, E{Key: k, Value: v})
}

// PullAll 删除数组内包含 v 数组内的所有元素
// https://www.mongodb.com/docs/manual/reference/operator/update/pullAll/
func (o *Updater) PullAll(k string, v A) {
	o.PullerAll = append(o.PullerAll, E{Key: k, Value: v})
}

func (o *Updater) SetCurrentDate(k string, v bool) {
	o.CurDate = append(o.CurDate, E{Key: k, Value: v})
}

func (o *Updater) Upsert(doc D) {
	o.SetOnInsert = doc
}

func (o *Updater) Done() D {
	op := D{}
	if len(o.CurDate) > 0 {
		op = append(op, E{Key: OptCurrentDate, Value: o.CurDate})
	}
	if len(o.Setter) > 0 {
		op = append(op, E{Key: OptSet, Value: o.Setter})
	}
	if len(o.UnSetter) > 0 {
		op = append(op, E{Key: OptUnset, Value: o.UnSetter})
	}
	if len(o.Pusher) > 0 {
		op = append(op, E{Key: OptPush, Value: o.Pusher})
	}
	if len(o.Puller) > 0 {
		op = append(op, E{Key: OptPull, Value: o.Puller})
	}
	if len(o.PullerAll) > 0 {
		op = append(op, E{Key: OptPullAll, Value: o.PullerAll})
	}
	if len(o.SetOnInsert) > 0 {
		op = append(op, E{Key: OptSetOnInsert, Value: o.SetOnInsert})
	}
	return op
}

// NewPipeline 管道聚合
// 请注意 pipe 顺序
func NewPipeline(pipe ...PipeCollection) Pipeline {
	p := make(Pipeline, len(pipe))
	for i := 0; i < len(pipe); i++ {
		p[i] = pipe[i].Pipeline()
	}
	return p
}
