// package store -- хранилище данных бота
package store

import (
	"fmt"

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

	"wartank/pkg/components/safebool"
	"wartank/pkg/types"
)

const (
	strStore = "store"
)

// Store -- хранилище данных
type Store struct {
	app    types.IServer
	db     *leveldb.DB
	isWork *safebool.SafeBool
}

// NewStore - -возвращает новый объект хранилища
func NewStore(app types.IServer) (*Store, error) {
	if app == nil {
		return nil, fmt.Errorf("NewStore(): IApp is nil")
	}
	sf := &Store{
		app:    app,
		isWork: safebool.NewSafeBool(),
	}
	if err := sf.open(); err != nil {
		return nil, fmt.Errorf("NewStore(): in open store, err=%w", err)
	}
	sf.isWork.Set()
	go sf.close()
	sf.app.Wg().Add(strStore)
	sf.app.Slog().Infof("NewStore(): run")
	return sf, nil
}

// Открытие хранилища
func (sf *Store) open() error {
	db, err := leveldb.OpenFile("store", nil)
	if err != nil {
		return fmt.Errorf("open(): in open store, err=%w", err)
	}
	sf.db = db
	return nil
}

// Get -- возвращает запись по ключу
func (sf *Store) Get(key string) (string, error) {
	if !sf.isWork.Get() {
		return "", fmt.Errorf("Store.Get():  store is close")
	}
	binData, err := sf.db.Get([]byte(key), nil)
	if err != nil {
		return "", fmt.Errorf("Store.Get(): in get by key(%q), err=%w", key, err)
	}
	return string(binData), nil
}

// Put -- помещает запись по ключу
func (sf *Store) Put(key string, val string) error {
	if !sf.isWork.Get() {
		return fmt.Errorf("Store.Put():  store is close")
	}
	if err := sf.db.Put([]byte(key), []byte(val), nil); err != nil {
		return fmt.Errorf("Store.Put():  in put key(%q), err=\n\t%w", key, err)
	}
	return nil
}

func (sf *Store) Find(prefix string) (map[string]string, error) {
	dictOut := make(map[string]string)
	iter := sf.db.NewIterator(&util.Range{Start: []byte(prefix)}, nil)
	for iter.Next() {
		key := iter.Key()
		val := iter.Value()
		dictOut[string(key)] = string(val)
	}
	iter.Release()
	err := iter.Error()
	if err != nil {
		return nil, fmt.Errorf("Store.Find(): in find by prefix(%q), err=\n\t%w", prefix, err)
	}
	return dictOut, nil
}

func (sf *Store) close() {
	<-sf.app.Done()
	if !sf.isWork.Get() {
		return
	}
	sf.app.Wg().Done(strStore)
	sf.isWork.Reset()
	sf.app.Slog().Infof("NewStore(): close")
}
