package writecachebadger

import (
	"context"
	"time"

	storagelog "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/internal/log"
	"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/internal/metaerr"
	"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/util/logicerr"
	"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/writecache"
	"git.frostfs.info/TrueCloudLab/frostfs-observability/tracing"
	apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
	oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
	"github.com/dgraph-io/badger/v4"
	"go.opentelemetry.io/otel/attribute"
	"go.opentelemetry.io/otel/trace"
)

// Delete removes object from write-cache.
//
// Returns an error of type apistatus.ObjectNotFound if object is missing in write-cache.
func (c *cache) Delete(ctx context.Context, addr oid.Address) error {
	_, span := tracing.StartSpanFromContext(ctx, "writecache.Delete",
		trace.WithAttributes(
			attribute.String("address", addr.EncodeToString()),
		))
	defer span.End()

	deleted := false
	storageType := writecache.StorageTypeUndefined
	startedAt := time.Now()
	defer func() {
		c.metrics.Delete(time.Since(startedAt), deleted, storageType)
	}()

	c.modeMtx.RLock()
	defer c.modeMtx.RUnlock()
	if c.readOnly() {
		return writecache.ErrReadOnly
	}

	key := addr2key(addr)

	err := c.db.Update(func(tx *badger.Txn) error {
		it, err := tx.Get(key[:])
		if err != nil {
			if err == badger.ErrKeyNotFound {
				return logicerr.Wrap(new(apistatus.ObjectNotFound))
			}
			return err
		}
		if it.ValueSize() > 0 {
			storageType = writecache.StorageTypeDB
			err := tx.Delete(key[:])
			if err == nil {
				storagelog.Write(c.log,
					storagelog.AddressField(addr.EncodeToString()),
					storagelog.StorageTypeField(wcStorageType),
					storagelog.OpField("db DELETE"),
				)
				deleted = true
				c.objCounters.DecDB()
			}
			return err
		}
		return nil
	})

	return metaerr.Wrap(err)
}