package svc

import (
	"time"

	"golib/v3/features/mo"
	"golib/v3/infra/ii"
	"golib/v3/log"
)

var (
	service    *Service
	permission ii.Permission
)

func InitDefault(client *mo.Client, items ii.Items, perms ii.Permission, log log.Logger) {
	service = &Service{
		Items:   items,
		Client:  client,
		Log:     log,
		Cache:   NewCache(items),
		Timeout: mo.DefaultTimout,
	}
	permission = perms
}

func SetTimeout(timeout time.Duration) {
	service.Timeout = timeout
}

func AddItemCache(name ii.Name) {
	service.Cache.AddItem(name)
	var data []mo.M
	if err := service.FindOneWith(name, &mo.Matcher{}, data); err != nil {
		panic(err)
	}
	service.Cache.SetData(name, data)
}

func With(u ii.User) *WithUser {
	return &WithUser{
		User:    u,
		Perms:   permission,
		Service: service,
	}
}

func GetItems() ii.Items {
	return service.GetItems()
}

func HasItem(name ii.Name) (*ii.ItemInfo, bool) {
	info, ok := service.HasItem(name)
	if !ok {
		return nil, false
	}
	return info, true
}

func Find(name ii.Name, filter mo.Filter) ([]*Row, error) {
	return service.Find(name, filter)
}

func FindWith(name ii.Name, filter mo.Filter, v any) error {
	return service.FindWith(name, filter, v)
}

func FindOne(name ii.Name, filter mo.Filter) (*Row, error) {
	return service.FindOne(name, filter)
}

func FindOneWith(name ii.Name, filter mo.Filter, v any) error {
	return service.FindOneWith(name, filter, v)
}

func FindOneAndDelete(name ii.Name, filter mo.Filter) error {
	return service.FindOneAndDelete(name, filter)
}

func DeleteOne(name ii.Name, filter mo.Filter) error {
	return service.DeleteOne(name, filter)
}

func DeleteMany(name ii.Name, filter mo.Filter) error {
	return service.DeleteMany(name, filter)
}

func FindOneAndUpdate(name ii.Name, filter, update mo.Filter) error {
	return service.FindOneAndUpdate(name, filter, update)
}

func EstimatedDocumentCount(name ii.Name) (int64, error) {
	return service.EstimatedDocumentCount(name)
}

func CountDocuments(name ii.Name, filter mo.Filter) (int64, error) {
	return service.CountDocuments(name, filter)
}

func InsertOne(name ii.Name, doc any) (mo.ObjectID, error) {
	var data mo.M
	if v, ok := doc.(mo.M); ok {
		data = v
	} else {
		if err := mo.Decode(doc, &data); err != nil {
			return mo.NilObjectID, err
		}
	}
	return service.InsertOne(name, data)
}

func InsertMany(name ii.Name, docs mo.A) (mo.A, error) {
	return service.InsertMany(name, docs)
}

func UpdateOne(name ii.Name, filter, update mo.Filter) error {
	return service.UpdateOne(name, filter, update)
}

func UpdateByID(name ii.Name, id mo.ObjectID, update mo.Filter) error {
	return service.UpdateByID(name, id, update)
}

func UpdateMany(name ii.Name, filter, update mo.Filter) error {
	return service.UpdateMany(name, filter, update)
}

func Aggregate(name ii.Name, pipe mo.Pipeline, v any) error {
	return service.Aggregate(name, pipe, v)
}
