package localserver

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

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

type BasicRights struct {
	Create bool `json:"create"`
}

type ChatRights struct {
	*BasicRights
}

type AudioChannelRights struct {
	*BasicRights
}

type VideoChannelRights struct {
	*BasicRights
}

type MediaRights struct {
	*BasicRights
}

type FileRights struct {
	*BasicRights
	CreateAtRoot bool `json:"createAtRoot"`
}

type User struct {
	ID                   string              `json:"id"`
	Name                 string              `json:"name"`
	ChatRights           *ChatRights         `json:"chatRights"`
	AudioChannelRights   *AudioChannelRights `json:"audioChannelRights"`
	VideoChannelRights   *VideoChannelRights `json:"videoChannelRights"`
	MediaRights          *MediaRights        `json:"mediaRights"`
	FileRights           *FileRights         `json:"fileRights"`
	KnownChatsId         []string            `json:"knownChatsId"`
	KnownAudioChannelsId []string            `json:"knownAudioChannelsId"`
	KnownVideoChannelsId []string            `json:"knownVideoChannelsId"`
	KnownMediaFolderId   []string            `json:"knownMediaFolderId"`
	KnownFileFolderId    []string            `json:"knownFileFolderId"`
	Admin                bool                `json:"admin"`
	Owner                bool                `json:"owner"`
}

type ZoneUsersDBHandler struct {
	ZoneID string
	db     func(func(*badger.DB) (err error)) (err error)
}

func NewChatRights(create bool) *ChatRights {
	return &ChatRights{
		BasicRights: &BasicRights{
			Create: create,
		},
	}
}

func NewAudioChannelRights(create bool) *AudioChannelRights {
	return &AudioChannelRights{
		BasicRights: &BasicRights{
			Create: create,
		},
	}
}

func NewVideoChannelRights(create bool) *VideoChannelRights {
	return &VideoChannelRights{
		BasicRights: &BasicRights{
			Create: create,
		},
	}
}

func NewMediaRights(create bool) *MediaRights {
	return &MediaRights{
		BasicRights: &BasicRights{
			Create: create,
		},
	}
}

func NewFileRights(create bool) *FileRights {
	return &FileRights{
		BasicRights: &BasicRights{
			Create: create,
		},
	}
}

func NewZoneUsersDBHandler(zoneId string, owner string, authorizedMembers []string, init bool) (zoneChatDBHandler *ZoneUsersDBHandler, err error) {
	db := func(f func(*badger.DB) (err error)) (err error) {
		db, err := badger.Open(badger.DefaultOptions(filepath.Join(dataPath, "data", "zones", zoneId, "users")).WithLogger(dbLogger))
		if err != nil {
			return
		}
		defer db.Close()
		err = f(db)
		return
	}
	if init {
		admin := &User{
			ID:                   owner,
			Name:                 owner,
			ChatRights:           NewChatRights(true),
			AudioChannelRights:   NewAudioChannelRights(true),
			VideoChannelRights:   NewVideoChannelRights(true),
			MediaRights:          NewMediaRights(true),
			FileRights:           NewFileRights(true),
			KnownChatsId:         []string{},
			KnownAudioChannelsId: []string{},
			KnownVideoChannelsId: []string{},
			KnownMediaFolderId:   make([]string, 0),
			KnownFileFolderId:    make([]string, 0),
			Admin:                true,
			Owner:                true,
		}
		bs, jsonErr := json.Marshal(admin)
		if jsonErr != nil {
			return nil, jsonErr
		}
		if err = db(func(d *badger.DB) (err error) {
			err = d.Update(func(txn *badger.Txn) (err error) {
				err = txn.Set([]byte(owner), bs)
				return
			})
			return
		}); err != nil {
			return
		}
	}
	bs, err := os.ReadFile(filepath.Join(dataPath, "data", "zones", zoneId, "users", "usersConfig.json"))
	if err != nil {
		return
	}
	var baseUserConfig ZoneUserConfig
	if err = json.Unmarshal(bs, &baseUserConfig); err != nil {
		return
	}
	if err = db(func(d *badger.DB) (err error) {
		err = d.Update(func(txn *badger.Txn) (err error) {
			for _, member := range authorizedMembers {
				if member != owner {
					if _, berr := txn.Get([]byte(member)); berr == badger.ErrKeyNotFound {
						logger.Println("creating new user", member)
						user := &User{
							ID:                   member,
							Name:                 member,
							ChatRights:           baseUserConfig.DefaultChatRights,
							AudioChannelRights:   baseUserConfig.DefaultAudioChannelRights,
							VideoChannelRights:   baseUserConfig.DefaultVideoChannelRights,
							MediaRights:          baseUserConfig.DefaultMediaRights,
							FileRights:           baseUserConfig.DefaultFileRights,
							KnownChatsId:         []string{},
							KnownAudioChannelsId: []string{},
							KnownVideoChannelsId: []string{},
							KnownMediaFolderId:   make([]string, 0),
							KnownFileFolderId:    make([]string, 0),
							Admin:                false,
							Owner:                false,
						}
						bs, jsonErr := json.Marshal(user)
						if jsonErr != nil {
							return jsonErr
						}
						if err = txn.Set([]byte(member), bs); err != nil {
							return
						}
					}
				}
			}
			return nil
		})
		return
	}); err != nil {
		return
	}
	zoneChatDBHandler = &ZoneUsersDBHandler{
		db:     db,
		ZoneID: zoneId,
	}
	return
}

func (zcdbh *ZoneUsersDBHandler) AddNewUser(user *User) (err error) {
	bs, err := json.Marshal(user)
	if err != nil {
		return
	}
	if err = zcdbh.db(func(d *badger.DB) (err error) {
		err = d.Update(func(txn *badger.Txn) error {
			if _, notFoundErr := txn.Get([]byte(user.ID)); notFoundErr == badger.ErrKeyNotFound {
				if updateErr := txn.Set([]byte(user.ID), bs); updateErr != nil {
					return updateErr
				}
			}
			return nil
		})
		return
	}); err != nil {
		return
	}
	return
}

func (zcdbh *ZoneUsersDBHandler) DeleteUser(id string) (err error) {
	err = zcdbh.db(func(d *badger.DB) (err error) {
		err = d.Update(func(txn *badger.Txn) (err error) {
			err = txn.Delete([]byte(id))
			return
		})
		return
	})
	return
}

func (zcdbh *ZoneUsersDBHandler) ListUsers(lastIndex int, limit int) (users []*User, err error) {
	err = zcdbh.db(func(d *badger.DB) (err error) {
		err = d.View(func(txn *badger.Txn) error {
			opt := badger.DefaultIteratorOptions
			opt.Reverse = true
			it := txn.NewIterator(opt)
			defer it.Close()
			users = []*User{}
			for it.Rewind(); it.Valid(); it.Next() {
				item := it.Item()
				if err = item.Value(func(val []byte) error {
					var user *User
					if jsonErr := json.Unmarshal(val, &user); jsonErr != nil {
						return jsonErr
					}
					users = append(users, user)
					return nil
				}); err != nil {
					return err
				}
			}
			return nil
		})
		return
	})
	return
}

func (zcdbh *ZoneUsersDBHandler) GetUser(id string) (user *User, err error) {
	err = zcdbh.db(func(d *badger.DB) (err error) {
		err = d.View(func(txn *badger.Txn) error {
			item, err := txn.Get([]byte(id))
			if err != nil {
				return err
			}
			err = item.Value(func(val []byte) (err error) {
				err = json.Unmarshal(val, &user)
				return
			})
			return err
		})
		return
	})
	return
}

func (zcdbh *ZoneUsersDBHandler) ModifyUser(id string, user *User) (err error) {
	bs, err := json.Marshal(user)
	if err != nil {
		return
	}
	err = zcdbh.db(func(d *badger.DB) (err error) {
		err = d.Update(func(txn *badger.Txn) (err error) {
			err = txn.Set([]byte(id), bs)
			return
		})
		return
	})
	return
}
