package storage

import (
	"encoding/json"
	"fmt"
	"intent-system/pkg/itypes"
	"os"
	"strings"

	"github.com/civet148/log"
	"github.com/syndtr/goleveldb/leveldb"
)

const (
	LEVEL_DB_KEY_PREFFIX_CONTEXT = "/context/" //the last '/' cannot be removed
)

type ContextMap map[string]*itypes.Context

type LocalStorage struct {
	ldb *leveldb.DB
}

var Store *LocalStorage

func init() {
	Store = NewLocalStorage()
}

func NewLocalStorage() *LocalStorage {
	return &LocalStorage{}
}

func (s *LocalStorage) open() (err error) {
	s.ldb, err = leveldb.OpenFile(itypes.DefaultLevelDBHome, nil)
	if err != nil {
		return fmt.Errorf("open level db path [%s] error [%s]", itypes.DefaultLevelDBHome, err.Error())
	}
	return
}

func (s *LocalStorage) close() (err error) {
	return s.ldb.Close()
}

func (s *LocalStorage) delete(strKey string) (err error) {
	return s.ldb.Delete([]byte(strKey), nil)
}

func (s *LocalStorage) put(strKey string, data []byte) (err error) {
	if err = s.ldb.Put([]byte(strKey), data, nil); err != nil {
		log.Errorf("level db put [%s] error [%s]", strKey, err)
		return
	}
	return
}

func (s *LocalStorage) clean() error {
	return os.RemoveAll(itypes.DefaultLevelDBHome)
}

func (s *LocalStorage) get(strKey string) []byte {
	data, err := s.ldb.Get([]byte(strKey), nil)
	if err != nil {
		log.Errorf("level db get [%s] error [%s]", strKey, err)
		return nil
	}
	return data
}

// clean storage directory
func (s *LocalStorage) Clean() error {
	return s.clean()
}

func (s *LocalStorage) DeleteContext(strAuthToken string) {
	var strKey = s.MakeContextKey(strAuthToken)
	if err := s.open(); err != nil {
		log.Errorf(err.Error())
		return
	}
	defer s.close()
	if err := s.delete(strKey); err != nil {
		log.Errorf(err.Error())
	}
}

func (s *LocalStorage) PutContext(ctx *itypes.Context) (err error) {
	var data []byte
	data, err = json.Marshal(ctx)
	if err != nil {
		log.Errorf("json marshal error [%s]", err)
		return
	}
	if err = s.open(); err != nil {
		log.Errorf(err.Error())
		return
	}
	defer s.close()
	var strKey = s.MakeContextKey(ctx.AuthToken())
	if err = s.put(strKey, data); err != nil {
		log.Errorf("level db put error [%s]", err)
		return
	}
	return
}

func (s *LocalStorage) LoadContexts() (contexts ContextMap, err error) {
	contexts = make(ContextMap)
	if err = s.open(); err != nil {
		log.Errorf(err.Error())
		return
	}
	defer s.close()
	iter := s.ldb.NewIterator(nil, nil)
	for iter.Next() {
		strKey := string(iter.Key())
		value := iter.Value()

		if strings.HasPrefix(strKey, LEVEL_DB_KEY_PREFFIX_CONTEXT) {
			strKey = strings.TrimPrefix(strKey, LEVEL_DB_KEY_PREFFIX_CONTEXT)
			var ctx = &itypes.Context{}
			if err = json.Unmarshal(value, ctx); err != nil {
				log.Errorf("key [%s] value unmarshal error [%s]", strKey, err)
				continue
			}
			contexts[strKey] = ctx
			log.Debugf("load user token [%v] context [%+v] from level db ok", strKey, ctx.Session)
		}
	}
	iter.Release()
	return
}

func (s *LocalStorage) MakeContextKey(strAuthToken string) string {
	strKey := fmt.Sprintf("%s%v", LEVEL_DB_KEY_PREFFIX_CONTEXT, strAuthToken)
	return strKey
}
