package blobovnicza

import (
	"bytes"
	"encoding/binary"

	"go.etcd.io/bbolt"
)

const (
	dataSizeAndItemsCountBufLength = 8
)

var (
	metaBucketName = []byte("META")
	dataSizeKey    = []byte("data_size")
	itemsCountKey  = []byte("items_count")
)

func isNonDataBucket(bucketName []byte) bool {
	return bytes.Equal(bucketName, incompletedMoveBucketName) || bytes.Equal(bucketName, metaBucketName)
}

func hasDataSize(tx *bbolt.Tx) (uint64, bool) {
	b := tx.Bucket(metaBucketName)
	if b == nil {
		return 0, false
	}
	v := b.Get(dataSizeKey)
	if v == nil {
		return 0, false
	}
	if len(v) != dataSizeAndItemsCountBufLength {
		return 0, false
	}
	return binary.LittleEndian.Uint64(v), true
}

func hasItemsCount(tx *bbolt.Tx) (uint64, bool) {
	b := tx.Bucket(metaBucketName)
	if b == nil {
		return 0, false
	}
	v := b.Get(itemsCountKey)
	if v == nil {
		return 0, false
	}
	if len(v) != dataSizeAndItemsCountBufLength {
		return 0, false
	}
	return binary.LittleEndian.Uint64(v), true
}

func saveDataSize(tx *bbolt.Tx, size uint64) error {
	b, err := tx.CreateBucketIfNotExists(metaBucketName)
	if err != nil {
		return err
	}
	buf := make([]byte, dataSizeAndItemsCountBufLength)
	binary.LittleEndian.PutUint64(buf, size)
	return b.Put(dataSizeKey, buf)
}

func saveItemsCount(tx *bbolt.Tx, count uint64) error {
	b, err := tx.CreateBucketIfNotExists(metaBucketName)
	if err != nil {
		return err
	}
	buf := make([]byte, dataSizeAndItemsCountBufLength)
	binary.LittleEndian.PutUint64(buf, count)
	return b.Put(itemsCountKey, buf)
}

func updateMeta(tx *bbolt.Tx, updateValues func(count, size uint64) (uint64, uint64)) error {
	b, err := tx.CreateBucketIfNotExists(metaBucketName)
	if err != nil {
		return err
	}

	var count uint64
	var size uint64

	v := b.Get(itemsCountKey)
	if v != nil {
		count = binary.LittleEndian.Uint64(v)
	}

	v = b.Get(dataSizeKey)
	if v != nil {
		size = binary.LittleEndian.Uint64(v)
	}

	count, size = updateValues(count, size)

	sizeBuf := make([]byte, dataSizeAndItemsCountBufLength)
	binary.LittleEndian.PutUint64(sizeBuf, size)
	if err := b.Put(dataSizeKey, sizeBuf); err != nil {
		return err
	}

	countBuf := make([]byte, dataSizeAndItemsCountBufLength)
	binary.LittleEndian.PutUint64(countBuf, count)
	return b.Put(itemsCountKey, countBuf)
}