package local

import (
	"sync/atomic"
	"time"

	"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/engine"
	"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard/mode"
	"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/metrics"
)

var (
	_ Limiter = &noopLimiter{}
	_ Limiter = &sizeLimiter{}
)

type Limiter interface {
	engine.MetricRegister
	IsFull() bool
}

func NewLimiter(maxSizeGB int64) Limiter {
	if maxSizeGB < 0 {
		panic("max size is negative")
	}
	if maxSizeGB == 0 {
		return &noopLimiter{}
	}
	return &sizeLimiter{
		maxSize:     maxSizeGB * 1024 * 1024 * 1024,
		currentSize: &atomic.Int64{},
	}
}

type sizeLimiter struct {
	maxSize     int64
	currentSize *atomic.Int64
}

func (*sizeLimiter) AddMethodDuration(method string, d time.Duration)                {}
func (*sizeLimiter) AddToContainerSize(cnrID string, size int64)                     {}
func (*sizeLimiter) AddToObjectCounter(shardID string, objectType string, delta int) {}
func (*sizeLimiter) ClearErrorCounter(shardID string)                                {}
func (*sizeLimiter) DeleteShardMetrics(shardID string)                               {}
func (*sizeLimiter) GC() metrics.GCMetrics                                           { return &noopGCMetrics{} }
func (*sizeLimiter) IncErrorCounter(shardID string)                                  {}
func (*sizeLimiter) SetMode(shardID string, mode mode.Mode)                          {}
func (*sizeLimiter) SetObjectCounter(shardID string, objectType string, v uint64)    {}
func (*sizeLimiter) WriteCache() metrics.WriteCacheMetrics                           { return &noopWriteCacheMetrics{} }
func (*sizeLimiter) DeleteContainerSize(cnrID string)                                {}
func (*sizeLimiter) DeleteContainerCount(cnrID string)                               {}
func (*sizeLimiter) SetContainerObjectCounter(_, _, _ string, _ uint64)              {}
func (*sizeLimiter) IncContainerObjectCounter(_, _, _ string)                        {}
func (*sizeLimiter) SubContainerObjectCounter(_, _, _ string, _ uint64)              {}
func (*sizeLimiter) IncRefillObjectsCount(_, _ string, _ int, _ bool)                {}
func (*sizeLimiter) SetRefillPercent(_, _ string, _ uint32)                          {}
func (*sizeLimiter) SetRefillStatus(_, _, _ string)                                  {}

func (sl *sizeLimiter) AddToPayloadCounter(shardID string, size int64) {
	sl.currentSize.Add(size)
}

func (sl *sizeLimiter) IsFull() bool {
	cur := sl.currentSize.Load()
	return cur > sl.maxSize
}

type noopLimiter struct{}

func (*noopLimiter) AddMethodDuration(method string, d time.Duration)                {}
func (*noopLimiter) AddToContainerSize(cnrID string, size int64)                     {}
func (*noopLimiter) AddToObjectCounter(shardID string, objectType string, delta int) {}
func (*noopLimiter) AddToPayloadCounter(shardID string, size int64)                  {}
func (*noopLimiter) ClearErrorCounter(shardID string)                                {}
func (*noopLimiter) DeleteShardMetrics(shardID string)                               {}
func (*noopLimiter) GC() metrics.GCMetrics                                           { return &noopGCMetrics{} }
func (*noopLimiter) IncErrorCounter(shardID string)                                  {}
func (*noopLimiter) SetMode(shardID string, mode mode.Mode)                          {}
func (*noopLimiter) SetObjectCounter(shardID string, objectType string, v uint64)    {}
func (*noopLimiter) WriteCache() metrics.WriteCacheMetrics                           { return &noopWriteCacheMetrics{} }
func (*noopLimiter) IsFull() bool                                                    { return false }
func (*noopLimiter) DeleteContainerSize(cnrID string)                                {}
func (*noopLimiter) DeleteContainerCount(cnrID string)                               {}
func (*noopLimiter) SetContainerObjectCounter(_, _, _ string, _ uint64)              {}
func (*noopLimiter) IncContainerObjectCounter(_, _, _ string)                        {}
func (*noopLimiter) SubContainerObjectCounter(_, _, _ string, _ uint64)              {}
func (*noopLimiter) IncRefillObjectsCount(_, _ string, _ int, _ bool)                {}
func (*noopLimiter) SetRefillPercent(_, _ string, _ uint32)                          {}
func (*noopLimiter) SetRefillStatus(_, _, _ string)                                  {}

type noopGCMetrics struct{}

func (*noopGCMetrics) AddDeletedCount(shardID string, deleted uint64, failed uint64)          {}
func (*noopGCMetrics) AddExpiredObjectCollectionDuration(string, time.Duration, bool, string) {}
func (*noopGCMetrics) AddInhumedObjectCount(shardID string, count uint64, objectType string)  {}
func (*noopGCMetrics) AddRunDuration(shardID string, d time.Duration, success bool)           {}

type noopWriteCacheMetrics struct{}

func (*noopWriteCacheMetrics) AddMethodDuration(_, _, _, _ string, _ bool, _ time.Duration) {}
func (*noopWriteCacheMetrics) Close(_, _ string)                                            {}
func (*noopWriteCacheMetrics) IncOperationCounter(_, _, _, _ string, _ metrics.NullBool)    {}
func (*noopWriteCacheMetrics) SetActualCount(_, _, _ string, count uint64)                  {}
func (*noopWriteCacheMetrics) SetEstimateSize(_, _, _ string, _ uint64)                     {}
func (*noopWriteCacheMetrics) SetMode(shardID string, mode string)                          {}