package vfs

import (
	"context"
	"os"
	"time"

	db2 "gitlink.org.cn/cloudream/jcs-pub/client/internal/db"
	"gitlink.org.cn/cloudream/jcs-pub/client/internal/mount/fuse"
	"gitlink.org.cn/cloudream/jcs-pub/client/internal/mount/vfs/cache"
	clitypes "gitlink.org.cn/cloudream/jcs-pub/client/types"
	"gorm.io/gorm"
)

type FuseRoot struct {
	vfs *Vfs
}

func newRoot(vfs *Vfs) *FuseRoot {
	return &FuseRoot{
		vfs: vfs,
	}
}

func (r *FuseRoot) PathComps() []string {
	return []string{}
}

func (r *FuseRoot) Name() string {
	return ""
}

func (r *FuseRoot) Size() int64 {
	return 0
}

func (r *FuseRoot) Perm() os.FileMode {
	return 0755
}

func (r *FuseRoot) ModTime() time.Time {
	return time.Now()
}

func (r *FuseRoot) IsDir() bool {
	return true
}

func (r *FuseRoot) SetModTime(time time.Time) error {
	return nil
}

// 如果不存在，应该返回ErrNotExists
func (r *FuseRoot) Child(ctx context.Context, name string) (fuse.FsEntry, error) {
	ca := r.vfs.cache.Stat([]string{name})

	if ca == nil {
		bkt, err := r.vfs.db.Bucket().GetByName(r.vfs.db.DefCtx(), name)
		if err == nil {
			dir := r.vfs.cache.LoadDir([]string{name}, &cache.CreateDirOption{
				ModTime: bkt.CreateTime,
			})
			if dir == nil {
				return nil, fuse.ErrNotExists
			}

			return newBucketFromCache(dir.Info(), r.vfs), nil
		}

		if err == gorm.ErrRecordNotFound {
			return nil, fuse.ErrNotExists
		}

		return nil, err
	}

	if ca.IsDir {
		return newBucketFromCache(*ca, r.vfs), nil
	}

	return newFileFromCache(*ca, r.vfs), nil
}

func (r *FuseRoot) Children(ctx context.Context) ([]fuse.FsEntry, error) {
	return r.listChildren()
}

func (r *FuseRoot) ReadChildren() (fuse.DirReader, error) {
	ens, err := r.listChildren()
	if err != nil {
		return nil, err
	}

	return newFuseDirReader(ens), nil
}

func (r *FuseRoot) listChildren() ([]fuse.FsEntry, error) {
	var ens []fuse.FsEntry

	infos := r.vfs.cache.StatMany([]string{})

	bkts, err := r.vfs.db.Bucket().GetAll(r.vfs.db.DefCtx())
	if err != nil {
		return nil, err
	}

	bktMap := make(map[string]*clitypes.Bucket)
	for _, bkt := range bkts {
		b := bkt
		bktMap[bkt.Name] = &b
	}

	for _, c := range infos {
		delete(bktMap, c.PathComps[len(c.PathComps)-1])

		if c.IsDir {
			ens = append(ens, newBucketFromCache(c, r.vfs))
		} else {
			ens = append(ens, newFileFromCache(c, r.vfs))
		}
	}

	// 将远端目录同步到本地缓存中，防止在给目录中的远端对象创建本地缓存时，顺便创建的目录的元数据不对的情况
	for _, bkt := range bktMap {
		dir := r.vfs.cache.LoadDir([]string{bkt.Name}, &cache.CreateDirOption{
			ModTime: bkt.CreateTime,
		})

		if dir == nil {
			continue
		}

		ens = append(ens, newBucketFromCache(dir.Info(), r.vfs))
	}

	return ens, nil
}

func (r *FuseRoot) NewDir(ctx context.Context, name string) (fuse.FsDir, error) {
	cache := r.vfs.cache.CreateDir([]string{name})
	if cache == nil {
		return nil, fuse.ErrPermission
	}

	// TODO 用户ID，失败了可以打个日志
	// TODO 生成系统事件
	// 不关注创建是否成功，仅尝试一下
	r.vfs.db.Bucket().Create(r.vfs.db.DefCtx(), name, cache.ModTime())

	return newBucketFromCache(cache.Info(), r.vfs), nil
}

func (r *FuseRoot) NewFile(ctx context.Context, name string, flags uint32) (fuse.FileHandle, uint32, error) {
	return newFile(r.vfs, ctx, name, r, flags)
}

func (r *FuseRoot) RemoveChild(ctx context.Context, name string) error {
	// TODO 生成系统事件
	db := r.vfs.db
	return r.vfs.db.DoTx(func(tx db2.SQLContext) error {

		bkt, err := db.Bucket().GetByName(tx, name)
		if err == nil {
			err := db.Package().HasPackageIn(tx, bkt.BucketID)
			if err == nil {
				return fuse.ErrNotEmpty
			}
			if err != gorm.ErrRecordNotFound {
				return err
			}

			// 不管是否成功
			db.Bucket().DeleteComplete(tx, bkt.BucketID)

		} else if err != gorm.ErrRecordNotFound {
			return err
		}

		return r.vfs.cache.Remove([]string{name})
	})
}

func (r *FuseRoot) MoveChild(ctx context.Context, oldName string, newName string, newParent fuse.FsDir) error {
	newParentNode := newParent.(FuseNode)
	newParentPath := newParentNode.PathComps()

	// 只允许同层级内改名
	if len(newParentPath) != 0 {
		return fuse.ErrNotSupported
	}

	d := r.vfs.db
	return d.DoTx(func(tx db2.SQLContext) error {
		_, err := d.Bucket().GetByName(tx, newName)
		if err == nil {
			// 目标节点已经存在，不能重命名，直接退出
			return fuse.ErrExists
		}

		oldBkt, err := d.Bucket().GetByName(tx, oldName)
		if err == nil {
			err = d.Bucket().Rename(tx, oldBkt.BucketID, newName)
			if err != nil {
				return err
			}
		} else if err != gorm.ErrRecordNotFound {
			return err
		}

		return r.vfs.cache.Move([]string{oldName}, []string{newName})
	})
}

var _ fuse.FsDir = (*FuseRoot)(nil)
var _ FuseNode = (*FuseRoot)(nil)
