package vfs

import (
	"os"
	"time"

	"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 FuseFileNode struct {
	vfs       *Vfs
	pathComps []string
	size      int64
	modTime   time.Time
	perm      os.FileMode
}

func newFileFromCache(info cache.CacheEntryInfo, vfs *Vfs) *FuseFileNode {
	return &FuseFileNode{
		vfs:       vfs,
		pathComps: info.PathComps,
		size:      info.Size,
		modTime:   info.ModTime,
		perm:      info.Perm,
	}
}

func newFileFromObject(vfs *Vfs, pathComps []string, obj clitypes.Object) *FuseFileNode {
	return &FuseFileNode{
		vfs:       vfs,
		pathComps: pathComps,
		size:      obj.Size,
		modTime:   obj.UpdateTime,
		perm:      os.FileMode(0755), // TODO Object元数据中是没有保存权限的
	}
}

func (n *FuseFileNode) Name() string {
	return n.pathComps[len(n.pathComps)-1]
}

func (n *FuseFileNode) Size() int64 {
	info := n.vfs.cache.Stat(n.pathComps)
	if info == nil {
		return n.size
	}

	return info.Size
}

func (n *FuseFileNode) Perm() os.FileMode {
	return n.perm
}

func (n *FuseFileNode) ModTime() time.Time {
	info := n.vfs.cache.Stat(n.pathComps)
	if info == nil {
		return n.modTime
	}

	return info.ModTime
}

func (n *FuseFileNode) IsDir() bool {
	return false
}

func (n *FuseFileNode) Truncate(size uint64) error {
	ch := n.loadCacheFile()
	if ch == nil {
		return fuse.ErrNotExists
	}
	defer ch.Release()

	if !ch.LevelUp(cache.LevelComplete) {
		return fuse.ErrIOError
	}

	return ch.Truncate(int64(size))
}

func (n *FuseFileNode) SetModTime(time time.Time) error {
	ch := n.loadCacheFile()
	if ch == nil {
		return fuse.ErrNotExists
	}
	defer ch.Release()

	if !ch.LevelUp(cache.LevelMetaLoaded) {
		return fuse.ErrIOError
	}

	return ch.SetModTime(time)
}

func (n *FuseFileNode) Open(flags uint32) (fuse.FileHandle, uint32, error) {
	ch := n.loadCacheFile()
	if ch == nil {
		// 如果文件不存在，也不进行创建，因为创建不应该调用这个接口
		return nil, 0, fuse.ErrNotExists
	}
	defer ch.Release()

	if !ch.LevelUp(cache.LevelComplete) {
		return nil, 0, fuse.ErrIOError
	}

	hd := ch.Open(flags)
	return newFileHandle(n, hd), flags, nil
}

func (n *FuseFileNode) loadCacheFile() *cache.CacheFile {
	if len(n.pathComps) <= 2 {
		return n.vfs.cache.LoadFile(n.pathComps, nil)
	}

	cdsObj, err := n.vfs.db.Object().GetByFullPath(n.vfs.db.DefCtx(), n.pathComps[0], n.pathComps[1], clitypes.JoinObjectPath(n.pathComps[2:]...))
	if err == nil {
		file := n.vfs.cache.LoadFile(n.pathComps, &cdsObj)
		if file == nil {
			return nil
		}

		return file
	}

	if err == gorm.ErrRecordNotFound {
		return n.vfs.cache.LoadFile(n.pathComps, nil)
	}

	return nil
}

type FuseFileHandle struct {
	entry *FuseFileNode
	chd   *cache.CacheFileHandle
}

func newFileHandle(entry *FuseFileNode, chd *cache.CacheFileHandle) *FuseFileHandle {
	return &FuseFileHandle{
		entry: entry,
		chd:   chd,
	}
}

func (hd *FuseFileHandle) Entry() fuse.FsFile {
	return hd.entry
}

func (hd *FuseFileHandle) ReadAt(buf []byte, off int64) (int, error) {
	return hd.chd.ReadAt(buf, off)
}

func (hd *FuseFileHandle) WriteAt(buf []byte, off int64) (int, error) {
	return hd.chd.WriteAt(buf, off)
}

func (hd *FuseFileHandle) Sync() error {
	return hd.chd.Sync()
}

func (hd *FuseFileHandle) Flush() error {
	return nil
}

func (hd *FuseFileHandle) Close() error {
	return hd.chd.Close()
}
