package persistence

import (
	"encoding/json"
	"git.openprivacy.ca/openprivacy/log"
	bolt "go.etcd.io/bbolt"
)

// BoltPersistence creates a persistence services backed by an on-disk bolt database
type BoltPersistence struct {
	db *bolt.DB
}

// Open opens a database
func (bp *BoltPersistence) Open(handle string) error {
	db, err := bolt.Open(handle, 0600, nil)
	bp.db = db
	log.Debugf("Loaded the Database")
	return err
}

// Setup initializes the given buckets if they do not exist in the database
func (bp *BoltPersistence) Setup(buckets []string) error {
	return bp.db.Update(func(tx *bolt.Tx) error {
		for _, bucket := range buckets {
			tx.CreateBucketIfNotExists([]byte(bucket))
		}
		return nil
	})
}

// Close closes the databases
func (bp *BoltPersistence) Close() {
	bp.db.Close()
}

// Persist stores a record in the database
func (bp *BoltPersistence) Persist(bucket string, name string, value interface{}) error {
	valueBytes, _ := json.Marshal(value)
	return bp.db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(bucket))
		b.Put([]byte(name), valueBytes)
		return nil
	})
}

// Check returns true if the record exists in the given bucket.
func (bp *BoltPersistence) Check(bucket string, name string) (bool, error) {
	log.Debugf("Checking database: %v %v", bucket, name)
	var val []byte
	err := bp.db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(bucket))
		val = b.Get([]byte(name))
		return nil
	})

	if err != nil {
		return false, err
	} else if val != nil {
		return true, nil
	}
	return false, nil
}

// Load reads a value from a given bucket.
func (bp *BoltPersistence) Load(bucket string, name string, value interface{}) error {
	var val []byte
	err := bp.db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(bucket))
		val = b.Get([]byte(name))
		return nil
	})
	if err != nil {
		return err
	}
	return json.Unmarshal(val, &value)
}
