package meta

import (
	cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
	"go.etcd.io/bbolt"
)

type bucketCache struct {
	locked    *bbolt.Bucket
	graveyard *bbolt.Bucket
	garbage   *bbolt.Bucket
	expired   map[cid.ID]*bbolt.Bucket
}

func newBucketCache() *bucketCache {
	return &bucketCache{expired: make(map[cid.ID]*bbolt.Bucket)}
}

func getLockedBucket(bc *bucketCache, tx *bbolt.Tx) *bbolt.Bucket {
	if bc == nil {
		return tx.Bucket(bucketNameLocked)
	}
	return getBucket(&bc.locked, tx, bucketNameLocked)
}

func getGraveyardBucket(bc *bucketCache, tx *bbolt.Tx) *bbolt.Bucket {
	if bc == nil {
		return tx.Bucket(graveyardBucketName)
	}
	return getBucket(&bc.graveyard, tx, graveyardBucketName)
}

func getGarbageBucket(bc *bucketCache, tx *bbolt.Tx) *bbolt.Bucket {
	if bc == nil {
		return tx.Bucket(garbageBucketName)
	}
	return getBucket(&bc.garbage, tx, garbageBucketName)
}

func getBucket(cache **bbolt.Bucket, tx *bbolt.Tx, name []byte) *bbolt.Bucket {
	if *cache != nil {
		return *cache
	}

	*cache = tx.Bucket(name)
	return *cache
}

func getExpiredBucket(bc *bucketCache, tx *bbolt.Tx, cnr cid.ID) *bbolt.Bucket {
	if bc == nil {
		bucketName := make([]byte, bucketKeySize)
		bucketName = objectToExpirationEpochBucketName(cnr, bucketName)
		return tx.Bucket(bucketName)
	}
	return getMappedBucket(bc.expired, tx, objectToExpirationEpochBucketName, cnr)
}

func getMappedBucket(m map[cid.ID]*bbolt.Bucket, tx *bbolt.Tx, nameFunc func(cid.ID, []byte) []byte, cnr cid.ID) *bbolt.Bucket {
	value, ok := m[cnr]
	if ok {
		return value
	}

	bucketName := make([]byte, bucketKeySize)
	bucketName = nameFunc(cnr, bucketName)
	m[cnr] = getBucket(&value, tx, bucketName)
	return value
}