package svc

import (
	"context"
	"time"

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

var (
	service    *Service
	permission ii.Permission
)

func Reset() {
	service = &Service{
		Items:   &ii.ItemIndex{},
		Log:     log.ConsoleWith(log.LevelInfo, 2),
		Context: context.Background(),
		Timeout: mo.DefaultTimout,
	}
	permission = &ii.PermsConfig{}
}

func init() {
	Reset()
}

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),
		Context: context.Background(),
		Timeout: mo.DefaultTimout,
	}
	permission = perms
}

// SetContext Temporary API
func SetContext(ctx context.Context) {
	service.Context = ctx
}

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

func AddItemCache(name ii.Name) {
	service.Cache.AddItem(name)
	var data []mo.M
	if err := service.FindWith(name, &mo.Matcher{}, nil, nil, 0, 0, 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 FindAll(name ii.Name) ([]Row, error) {
	return service.FindAll(name)
}

func FindWith(name ii.Name, filter, sort, project mo.Filter, skip, limit int64, v any) error {
	return service.FindWith(name, filter, sort, project, skip, limit, v)
}

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

func FindOneWith(name ii.Name, filter, sort, project mo.Filter, v any) error {
	return service.FindOneWith(name, filter, sort, project, 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) {
	return service.InsertOne(name, doc)
}

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)
}
