// package store -- хранилище на диске
package store

import (
	"fmt"
	"os"
	"strings"

	"github.com/syndtr/goleveldb/leveldb"
	"github.com/syndtr/goleveldb/leveldb/util"

	"git.p78su.freemyip.com/svi/gostore/pkg/types"
)

// StoreDisk -- хранилище на диске
type Store struct {
	serv types.IService
	db   *leveldb.DB
}

// NewStore -- возвращает новое хранилище на диске
func NewStore(serv types.IService) (*Store, error) {
	if serv == nil {
		return nil, fmt.Errorf("NewStore(): IService==nil")
	}
	_ = os.MkdirAll("./store/db", 0750)
	db, err := leveldb.OpenFile("./store/db", nil)
	if err != nil {
		if !strings.Contains(err.Error(), "leveldb: manifest corrupted") {
			return nil, fmt.Errorf("NewStore(): in create IStoreDisk, err=\n\t%w", err)
		}
		db, err = leveldb.RecoverFile("./stor/db", nil)
		if err != nil {
			return nil, fmt.Errorf("NewStore(): in recovery DB, err=\n\t%w", err)
		}
	}
	sf := &Store{
		serv: serv,
		db:   db,
	}
	go sf.close()
	return sf, nil
}

// Put -- размещает в хранилище ключ и значение
func (sf *Store) Put(key string, val []byte) error {
	if key == "" {
		return fmt.Errorf("Store.Put(): key is empty")
	}
	err := sf.db.Put([]byte(key), val, nil)
	if err != nil {
		return fmt.Errorf("Store.Put(): key=%q\terr=\n\t%w", key, err)
	}
	return nil
}

// Get -- возвращает значение ключа
func (sf *Store) Get(key string) ([]byte, error) {
	val, err := sf.db.Get([]byte(key), nil)
	if err != nil {
		return nil, fmt.Errorf("Store.Get(): key=%q\terr=\n\t%w", key, err)
	}
	return val, nil
}

// Find -- ищет ключи по префиксу
func (sf *Store) Find(prefixKey string) ([]string, error) {
	lstKey := []string{}
	iter := sf.db.NewIterator(util.BytesPrefix([]byte(prefixKey)), nil)
	for iter.Next() {
		key := iter.Key()
		lstKey = append(lstKey, string(key))
	}
	iter.Release()
	err := iter.Error()
	if err != nil {
		return nil, fmt.Errorf("Store.Find(): preefixKey=%q\terr=\n\t%w", prefixKey, err)
	}
	return lstKey, nil
}

// Del -- удаление ключа из базы
func (sf *Store) Del(key string) error {
	err := sf.db.Delete([]byte(key), nil)
	if err != nil {
		return fmt.Errorf("Store.Del(): key=%q\terr=\n\t%w", key, err)
	}
	return nil
}

// Ожидание закрытия приложения
func (sf *Store) close() {
	<-sf.serv.Ctx().Done()
	_ = sf.db.Close()
}
