package operator

import (
	"fmt"
	"local/dndex/storage/entity"
	"time"

	"go.mongodb.org/mongo-driver/bson"
)

type Unset string

func (u Unset) MarshalBSON() ([]byte, error) {
	return opMarshal("$unset", string(u), "")
}

type PopIf struct {
	Key    string
	Filter interface{}
}

func (pi PopIf) MarshalBSON() ([]byte, error) {
	return opMarshal("$pull", pi.Key, pi.Filter)
}

type SetMany struct {
	Value interface{}
}

func (s SetMany) MarshalBSON() ([]byte, error) {
	return opMarshal("$set", "", s.Value)
}

type Set struct {
	Key   string
	Value interface{}
}

func (s Set) MarshalBSON() ([]byte, error) {
	return opMarshal("$set", s.Key, s.Value)
}

type Push struct {
	Key   string
	Value interface{}
}

func (p Push) MarshalBSON() ([]byte, error) {
	return opMarshal("$push", p.Key, p.Value)
}

func opMarshal(op, key string, value interface{}) ([]byte, error) {
	marshalable := opMarshalable(op, key, value)
	if len(marshalable) == 0 {
		return nil, fmt.Errorf("failed marshalling op")
	}
	return bson.Marshal(marshalable)
}

func opMarshalable(op, key string, value interface{}) map[string]interface{} {
	if len(key) == 0 && value == nil {
		return nil
	}
	m := map[string]interface{}{
		op: map[string]interface{}{
			key: value,
		},
	}
	if len(key) == 0 {
		m[op] = value
	}
	if _, ok := m["$set"]; !ok {
		m["$set"] = map[string]interface{}{}
	}
	switch m["$set"].(type) {
	case map[string]interface{}:
		m["$set"].(map[string]interface{})[entity.Modified] = time.Now().UnixNano()
	case bson.M:
		m["$set"].(bson.M)[entity.Modified] = time.Now().UnixNano()
		//case primitive.M:
		//m["$set"].(primitive.M)[entity.Modified] = time.Now().UnixNano()
	}
	return m
}
