package policer

import (
	"sync"
	"time"

	"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger"
	oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
	lru "github.com/hashicorp/golang-lru/v2"
	"go.uber.org/zap"
)

type objectsInWork struct {
	sync.RWMutex
	objs map[oid.Address]struct{}
}

func (oiw *objectsInWork) inWork(addr oid.Address) bool {
	oiw.RLock()
	_, ok := oiw.objs[addr]
	oiw.RUnlock()

	return ok
}

func (oiw *objectsInWork) remove(addr oid.Address) {
	oiw.Lock()
	delete(oiw.objs, addr)
	oiw.Unlock()
}

func (oiw *objectsInWork) add(addr oid.Address) bool {
	oiw.Lock()
	_, exists := oiw.objs[addr]
	oiw.objs[addr] = struct{}{}
	oiw.Unlock()
	return !exists
}

// Policer represents the utility that verifies
// compliance with the object storage policy.
type Policer struct {
	*cfg

	cache *lru.Cache[oid.Address, time.Time]

	objsInWork *objectsInWork
}

// New creates, initializes and returns Policer instance.
func New(opts ...Option) *Policer {
	c := defaultCfg()

	for i := range opts {
		opts[i](c)
	}

	c.log = &logger.Logger{Logger: c.log.With(zap.String("component", "Object Policer"))}

	cache, err := lru.New[oid.Address, time.Time](int(c.cacheSize))
	if err != nil {
		panic(err)
	}

	return &Policer{
		cfg:   c,
		cache: cache,
		objsInWork: &objectsInWork{
			objs: make(map[oid.Address]struct{}, c.taskPool.Cap()),
		},
	}
}