package localserver

import (
	"encoding/json"
	"os"
	"path/filepath"

	"github.com/dgraph-io/badger/v3"
)

type ZoneFilesDBHandler struct {
	CurrentLocation string
	ZoneID          string
	rootPath        string
	db              func(string, func(*badger.DB) (err error)) (err error)
}

func NewZoneFilesDBHandler(zoneId string) (zoneFilesDBHandler *ZoneFilesDBHandler, err error) {
	zoneFilesDBHandler = &ZoneFilesDBHandler{
		db: func(path string, f func(*badger.DB) (err error)) (err error) {
			root := filepath.Join(dataPath, "data", "zones", zoneId, "fs", path)
			db, err := badger.Open(badger.DefaultOptions(root).WithLogger(dbLogger))
			if err != nil {
				return
			}
			defer db.Close()
			err = f(db)
			return
		},
		ZoneID: zoneId,
	}
	zoneFilesDBHandler.rootPath = filepath.Join(dataPath, "data", "zones", zoneId, "fs")
	return
}

func (zcdbh *ZoneFilesDBHandler) AddNewFSEntity(path string, fsEntity *ZoneFSEntity) (err error) {
	bs, err := json.Marshal(fsEntity)
	if err != nil {
		return
	}
	err = zcdbh.db(path, func(d *badger.DB) (err error) {
		err = d.Update(func(txn *badger.Txn) (err error) {
			if updateErr := txn.Set([]byte(fsEntity.Name), bs); updateErr != nil {
				return updateErr
			}
			if fsEntity.Folder {
				err = os.Mkdir(filepath.Join(dataPath, "data", "zones", zcdbh.ZoneID, "fs", path, fsEntity.Name), 0700)
			}
			return
		})
		return
	})
	return
}

func (zcdbh *ZoneFilesDBHandler) DeleteFolder(path, name string) (err error) {
	if err = zcdbh.db(path, func(d *badger.DB) (err error) {
		err = d.Update(func(txn *badger.Txn) (err error) {
			err = txn.Delete([]byte(name))
			return
		})
		return
	}); err != nil {
		return
	}
	err = os.RemoveAll(filepath.Join(zcdbh.rootPath, path, name))
	return
}

func (zcdbh *ZoneFilesDBHandler) DeleteFile(path, name string) (err error) {
	if err = zcdbh.db(path, func(d *badger.DB) (err error) {
		err = d.Update(func(txn *badger.Txn) (err error) {
			err = txn.Delete([]byte(name))
			return
		})
		return
	}); err != nil {
		return
	}
	err = os.Remove(filepath.Join(zcdbh.rootPath, path, "__files__", name))
	return
}

func (zcdbh *ZoneFilesDBHandler) ListZoneFSEntity(path string, userId string, lastIndex int, limit int) (fsEntities []*ZoneFSEntity, l int, err error) {
	err = zcdbh.db(path, func(d *badger.DB) (err error) {
		err = d.View(func(txn *badger.Txn) (err error) {
			opt := badger.DefaultIteratorOptions
			opt.Reverse = true
			it := txn.NewIterator(opt)
			defer it.Close()
			fsEntities = make([]*ZoneFSEntity, 0)
			splittedPath := filepath.SplitList(path)
			var parent *ZoneFSEntity
			if len(splittedPath) >= 2 {
				parent, err = zcdbh.GetFSEntity(filepath.Dir(path), filepath.Base(path))
			} else if len(splittedPath) > 0 {
				parent = nil
			}
			if err != nil {
				return
			}
			for it.Rewind(); it.Valid(); it.Next() {
				item := it.Item()
				if err = item.Value(func(val []byte) (err error) {
					var fsEntity *ZoneFSEntity
					if err = json.Unmarshal(val, &fsEntity); err != nil {
						return err
					}
					logger.Println("------------------", fsEntity.Name)
					if _, ok := fsEntity.Members[userId]; ok || fsEntity.Type == "public" {
						fsEntities = append(fsEntities, fsEntity)
					} else if parent != nil {
						if _, ok := parent.Members[userId]; ok && fsEntity.Type != "private" {
							if parent.Members[userId].Read {
								fsEntities = append(fsEntities, fsEntity)
							}
						}
					}
					return
				}); err != nil {
					return err
				}
			}
			return
		})
		return
	})
	return
}

func (zcdbh *ZoneFilesDBHandler) GetFSEntity(path string, folderName string) (folder *ZoneFSEntity, err error) {
	err = zcdbh.db(path, func(d *badger.DB) (err error) {
		err = d.View(func(txn *badger.Txn) (err error) {
			item, err := txn.Get([]byte(folderName))
			if err != nil {
				return
			}
			err = item.Value(func(val []byte) (err error) {
				err = json.Unmarshal(val, &folder)
				return
			})
			return
		})
		return
	})
	return
}

func (zcdbh *ZoneFilesDBHandler) SetFSEntity(path string, oldName string, fsEntity *ZoneFSEntity) (err error) {
	bs, err := json.Marshal(fsEntity)
	if err != nil {
		return
	}
	if err = zcdbh.db(path, func(d *badger.DB) (err error) {
		err = d.Update(func(txn *badger.Txn) (err error) {
			if dErr := txn.Delete([]byte(oldName)); err != nil {
				logger.Println("error from here:", dErr)
			}
			if updateErr := txn.Set([]byte(fsEntity.Name), bs); updateErr != nil {
				return updateErr
			}
			return
		})
		return
	}); err != nil {
		return
	}
	return
}
