package filedb

import (
	"fmt"
	"gadmin/internal/admin/forms"
	"gadmin/utility"
	"sync"

	"github.com/google/btree"
	"github.com/spf13/cast"
)

type Node struct {
	Pk  int64       `json:"pk"`  // 索引
	Ref interface{} `json:"ref"` // 引用
}

type ider interface {
	ID() int64
	SetID(int64)
}

func (node *Node) Less(item btree.Item) bool {
	return node.Pk < (item.(*Node)).Pk
}

type Collection struct {
	AutoIncrement int64                       `json:"ai"`
	Idx           *btree.BTree                `json:"-"`
	Data          map[string]*forms.EmailItem `json:"data"`
	IdxMap        map[string]*btree.BTree     `json:"-"` // 索引
	lock          sync.Mutex                  `json:"-"`
	Name          string                      `json:"name"`
}

func NewCollection(name string) *Collection {
	return &Collection{
		AutoIncrement: 1,
		Idx:           btree.New(2),
		Name:          name,
		Data:          make(map[string]*forms.EmailItem, 0),
	}
}

func (db *Collection) LoadDb() (err error) {
	err = utility.LoadDb(&db.lock, db.Name, db)
	if err != nil {
		return err
	}
	for k, v := range db.Data {
		if v == nil {
			delete(db.Data, k)
			db.Idx.Delete(&Node{Pk: cast.ToInt64(k)})
		} else {
			db.Idx.ReplaceOrInsert(&Node{Pk: cast.ToInt64(k)})
		}

	}
	return
}

func (db *Collection) SaveDb() error {
	return utility.SaveDb(&db.lock, db, db.Name)
}

func (db *Collection) Add(row *forms.EmailItem) error {
	db.lock.Lock()
	defer db.lock.Unlock()
	if row.Id == 0 {
		row.SetID(db.AutoIncrement)
	}

	db.Data[cast.ToString(row.Id)] = row
	db.AutoIncrement++

	db.Idx.ReplaceOrInsert(&Node{Pk: row.ID()})
	return nil
}

func (db *Collection) Remove(Id int64) error {
	if item := db.Idx.Delete(&Node{Pk: Id}); item == nil {
		return fmt.Errorf("不存在该值")
	}

	delete(db.Data, cast.ToString(Id))
	return nil
}

func (db *Collection) All() (data []interface{}, err error) {
	db.Idx.Descend(func(i btree.Item) bool {
		id := i.(*Node).Pk
		if item, ok := db.Data[cast.ToString(id)]; ok {
			data = append(data, item)
		}
		return true
	})
	return
}

func (db *Collection) Find(id int64) (data interface{}, err error) {
	db.lock.Lock()
	defer db.lock.Unlock()

	item := db.Idx.Get(&Node{Pk: id})
	if v, ok := item.(*Node); ok {
		data = db.Data[cast.ToString(v.Pk)]
	}

	return
}
