package store

import (
	"io"

	"github.com/cockroachdb/pebble"
	"source.quilibrium.com/quilibrium/monorepo/node/config"
)

type PebbleDB struct {
	db *pebble.DB
}

func NewPebbleDB(config *config.DBConfig) *PebbleDB {
	db, err := pebble.Open(config.Path, &pebble.Options{})
	if err != nil {
		panic(err)
	}

	return &PebbleDB{db}
}

func (p *PebbleDB) Get(key []byte) ([]byte, io.Closer, error) {
	return p.db.Get(key)
}

func (p *PebbleDB) Set(key, value []byte) error {
	return p.db.Set(key, value, &pebble.WriteOptions{Sync: true})
}

func (p *PebbleDB) Delete(key []byte) error {
	return p.db.Delete(key, &pebble.WriteOptions{Sync: true})
}

func (p *PebbleDB) NewBatch() Transaction {
	return &PebbleTransaction{
		b: p.db.NewBatch(),
	}
}

func (p *PebbleDB) NewIter(lowerBound []byte, upperBound []byte) (
	Iterator,
	error,
) {
	return p.db.NewIter(&pebble.IterOptions{
		LowerBound: lowerBound,
		UpperBound: upperBound,
	})
}

func (p *PebbleDB) Compact(start, end []byte, parallelize bool) error {
	return p.db.Compact(start, end, parallelize)
}

func (p *PebbleDB) Close() error {
	return p.db.Close()
}

func (p *PebbleDB) DeleteRange(start, end []byte) error {
	return p.db.DeleteRange(start, end, &pebble.WriteOptions{Sync: true})
}

var _ KVDB = (*PebbleDB)(nil)

type Transaction interface {
	Set(key []byte, value []byte) error
	Commit() error
	Delete(key []byte) error
	Abort() error
}

type PebbleTransaction struct {
	b *pebble.Batch
}

func (t *PebbleTransaction) Set(key []byte, value []byte) error {
	return t.b.Set(key, value, &pebble.WriteOptions{Sync: true})
}

func (t *PebbleTransaction) Commit() error {
	return t.b.Commit(&pebble.WriteOptions{Sync: true})
}

func (t *PebbleTransaction) Delete(key []byte) error {
	return t.b.Delete(key, &pebble.WriteOptions{Sync: true})
}

func (t *PebbleTransaction) Abort() error {
	return t.b.Close()
}

var _ Transaction = (*PebbleTransaction)(nil)

func rightAlign(data []byte, size int) []byte {
	l := len(data)

	if l == size {
		return data
	}

	if l > size {
		return data[l-size:]
	}

	pad := make([]byte, size)
	copy(pad[size-l:], data)
	return pad
}