package mountablefs

import (
	"fmt"
	"io"
	"strings"
	"sync"
	"sync/atomic"
	"time"

	"github.com/c4pt0r/agfs/agfs-server/pkg/filesystem"
	"github.com/c4pt0r/agfs/agfs-server/pkg/plugin"
	"github.com/c4pt0r/agfs/agfs-server/pkg/plugin/api"
	"github.com/c4pt0r/agfs/agfs-server/pkg/plugin/loader"
	iradix "github.com/hashicorp/go-immutable-radix"
	log "github.com/sirupsen/logrus"
)

// Meta values for MountableFS
const (
	MetaValueRoot       = "root"
	MetaValueMountPoint = "mount-point"
)

// MountPoint represents a mounted service plugin
type MountPoint struct {
	Path   string
	Plugin plugin.ServicePlugin
	Config map[string]interface{} // Plugin configuration
}

// PluginFactory is a function that creates a new plugin instance
type PluginFactory func() plugin.ServicePlugin

// MountableFS is a FileSystem that supports mounting service plugins at specific paths
type MountableFS struct {
	// mountTree stores the radix tree for mount routing.
	// We use atomic.Value to store *iradix.Tree to enable lock-free reads.
	mountTree atomic.Value

	pluginFactories    map[string]PluginFactory
	pluginLoader       *loader.PluginLoader // For loading external plugins
	pluginNameCounters map[string]int       // Track counters for plugin names
	mu                 sync.RWMutex         // Protects write operations (Mount/Unmount) and plugin factories

	// Global handle ID management (prevents conflicts across multiple plugin instances)
	globalHandleID atomic.Int64 // Atomic counter for generating globally unique handle IDs

	// handleInfo stores the mapping between global handle IDs and underlying handles
	// Key: global handle ID (generated by MountableFS)
	// Value: handleInfo containing mount point and local handle ID
	handleInfos   map[int64]*handleInfo
	handleInfosMu sync.RWMutex
}

// handleInfo stores information about a handle, including its mount point and local handle
type handleInfo struct {
	mount       *MountPoint           // The mount point where this handle was opened
	localHandle filesystem.FileHandle // The underlying handle from the plugin
}

// NewMountableFS creates a new mountable file system with the specified WASM pool configuration
func NewMountableFS(poolConfig api.PoolConfig) *MountableFS {
	mfs := &MountableFS{
		pluginFactories:    make(map[string]PluginFactory),
		pluginLoader:       loader.NewPluginLoader(poolConfig),
		pluginNameCounters: make(map[string]int),
		handleInfos:        make(map[int64]*handleInfo),
	}
	mfs.mountTree.Store(iradix.New())
	// Start global handle IDs from 1
	mfs.globalHandleID.Store(0)
	return mfs
}

// GetPluginLoader returns the plugin loader instance
func (mfs *MountableFS) GetPluginLoader() *loader.PluginLoader {
	return mfs.pluginLoader
}

// RenamedPlugin wraps a plugin with a different name
type RenamedPlugin struct {
	plugin.ServicePlugin
	originalName string
	renamedName  string
}

// Name returns the renamed plugin name
func (rp *RenamedPlugin) Name() string {
	return rp.renamedName
}

// OriginalName returns the original plugin name
func (rp *RenamedPlugin) OriginalName() string {
	return rp.originalName
}

// generateUniquePluginName generates a unique plugin name with incremental suffix
// Must be called with mfs.mu held (write lock)
func (mfs *MountableFS) generateUniquePluginName(baseName string) string {
	// Check if base name is available
	if _, exists := mfs.pluginFactories[baseName]; !exists {
		// Base name is available, initialize counter
		mfs.pluginNameCounters[baseName] = 0
		return baseName
	}

	// Base name exists, increment counter and generate new name
	mfs.pluginNameCounters[baseName]++
	counter := mfs.pluginNameCounters[baseName]
	newName := fmt.Sprintf("%s-%d", baseName, counter)

	// Ensure the generated name doesn't conflict (defensive programming)
	for {
		if _, exists := mfs.pluginFactories[newName]; !exists {
			return newName
		}
		mfs.pluginNameCounters[baseName]++
		counter = mfs.pluginNameCounters[baseName]
		newName = fmt.Sprintf("%s-%d", baseName, counter)
	}
}

// RegisterPluginFactory registers a plugin factory for dynamic mounting
func (mfs *MountableFS) RegisterPluginFactory(name string, factory PluginFactory) {
	mfs.mu.Lock()
	defer mfs.mu.Unlock()
	mfs.pluginFactories[name] = factory
}

// CreatePlugin creates a plugin instance from a registered factory
func (mfs *MountableFS) CreatePlugin(name string) plugin.ServicePlugin {
	mfs.mu.RLock()
	defer mfs.mu.RUnlock()

	factory, ok := mfs.pluginFactories[name]
	if !ok {
		return nil
	}
	return factory()
}

// Mount mounts a service plugin at the specified path
func (mfs *MountableFS) Mount(path string, plugin plugin.ServicePlugin) error {
	mfs.mu.Lock()
	defer mfs.mu.Unlock()

	// Normalize path
	path = filesystem.NormalizePath(path)

	// Load current tree
	tree := mfs.mountTree.Load().(*iradix.Tree)

	// Check if path is already mounted
	if _, exists := tree.Get([]byte(path)); exists {
		return filesystem.NewAlreadyExistsError("mount", path)
	}

	// Special handling for plugins that need parent filesystem reference
	type parentFSSetter interface {
		SetParentFileSystem(filesystem.FileSystem)
	}
	if setter, ok := plugin.(parentFSSetter); ok {
		setter.SetParentFileSystem(mfs)
		log.Debugf("Set parentFS for plugin at %s", path)
	}

	// Create new tree with added mount
	newTree, _, _ := tree.Insert([]byte(path), &MountPoint{
		Path:   path,
		Plugin: plugin,
		Config: make(map[string]interface{}),
	})

	// Atomically update tree
	mfs.mountTree.Store(newTree)

	return nil
}

// MountPlugin dynamically mounts a plugin at the specified path
func (mfs *MountableFS) MountPlugin(fstype string, path string, config map[string]interface{}) error {
	mfs.mu.Lock()
	defer mfs.mu.Unlock()

	// Normalize path
	path = filesystem.NormalizePath(path)

	// Load current tree
	tree := mfs.mountTree.Load().(*iradix.Tree)

	// Check if path is already mounted
	if _, exists := tree.Get([]byte(path)); exists {
		return filesystem.NewAlreadyExistsError("mount", path)
	}

	// Get plugin factory
	factory, ok := mfs.pluginFactories[fstype]
	if !ok {
		return fmt.Errorf("unknown filesystem type: %s", fstype)
	}

	// Create plugin instance
	pluginInstance := factory()

	// Special handling for plugins that need rootFS reference
	type rootFSSetter interface {
		SetRootFS(filesystem.FileSystem)
	}
	if setter, ok := pluginInstance.(rootFSSetter); ok {
		setter.SetRootFS(mfs)
		log.Debugf("Set rootFS for plugin %s at %s", fstype, path)
	}

	// Special handling for plugins that need parent filesystem reference
	type parentFSSetter interface {
		SetParentFileSystem(filesystem.FileSystem)
	}
	if setter, ok := pluginInstance.(parentFSSetter); ok {
		setter.SetParentFileSystem(mfs)
		log.Debugf("Set parentFS for plugin %s at %s", fstype, path)
	}

	// Inject mount_path into config
	configWithPath := make(map[string]interface{})
	for k, v := range config {
		configWithPath[k] = v
	}
	configWithPath["mount_path"] = path

	// Validate plugin configuration
	if err := pluginInstance.Validate(configWithPath); err != nil {
		return fmt.Errorf("failed to validate plugin: %v", err)
	}

	// Initialize plugin with config
	if err := pluginInstance.Initialize(configWithPath); err != nil {
		return fmt.Errorf("failed to initialize plugin: %v", err)
	}

	// Create new tree with added mount
	newTree, _, _ := tree.Insert([]byte(path), &MountPoint{
		Path:   path,
		Plugin: pluginInstance,
		Config: config,
	})

	// Atomically update tree
	mfs.mountTree.Store(newTree)

	log.Infof("mounted %s at %s", fstype, path)
	return nil
}

// Unmount unmounts a plugin from the specified path
func (mfs *MountableFS) Unmount(path string) error {
	mfs.mu.Lock()
	defer mfs.mu.Unlock()

	path = filesystem.NormalizePath(path)

	// Load current tree
	tree := mfs.mountTree.Load().(*iradix.Tree)

	val, exists := tree.Get([]byte(path))
	if !exists {
		return fmt.Errorf("no mount at path: %s", path)
	}
	mount := val.(*MountPoint)

	// Shutdown the plugin
	if err := mount.Plugin.Shutdown(); err != nil {
		return fmt.Errorf("failed to shutdown plugin: %v", err)
	}

	// Create new tree without the mount
	newTree, _, _ := tree.Delete([]byte(path))

	// Atomically update tree
	mfs.mountTree.Store(newTree)

	log.Infof("Unmounted plugin at %s", path)
	return nil
}

// LoadExternalPluginWithType loads a plugin with an explicitly specified type
func (mfs *MountableFS) LoadExternalPluginWithType(libraryPath string, pluginType loader.PluginType) (plugin.ServicePlugin, error) {
	// For WASM plugins, pass MountableFS as host filesystem to allow access to all agfs paths
	var p plugin.ServicePlugin
	var err error
	if pluginType == loader.PluginTypeWASM {
		log.Infof("Loading WASM plugin with host filesystem access to all agfs paths")
		p, err = mfs.pluginLoader.LoadPluginWithType(libraryPath, pluginType, mfs)
	} else {
		p, err = mfs.pluginLoader.LoadPluginWithType(libraryPath, pluginType)
	}
	if err != nil {
		return nil, err
	}

	// Register the plugin as a factory so it can be mounted
	pluginName := p.Name()
	mfs.RegisterPluginFactory(pluginName, func() plugin.ServicePlugin {
		return p
	})

	log.Infof("Registered external plugin factory: %s (type: %s)", pluginName, pluginType)
	return p, nil
}

// LoadExternalPlugin loads a plugin from a shared library file
func (mfs *MountableFS) LoadExternalPlugin(libraryPath string) (plugin.ServicePlugin, error) {
	// Detect plugin type first
	pluginType, err := loader.DetectPluginType(libraryPath)
	if err != nil {
		return nil, fmt.Errorf("failed to detect plugin type: %w", err)
	}

	if pluginType == loader.PluginTypeWASM {
		return mfs.LoadExternalPluginWithType(libraryPath, pluginType)
	}

	p, err := mfs.pluginLoader.LoadPlugin(libraryPath)
	if err != nil {
		return nil, err
	}

	originalName := p.Name()

	mfs.mu.Lock()

	finalName := mfs.generateUniquePluginName(originalName)
	renamed := (finalName != originalName)

	if renamed {
		log.Infof("Plugin name '%s' already exists, using '%s' instead", originalName, finalName)
	}

	var pluginToRegister plugin.ServicePlugin = p
	if renamed {
		pluginToRegister = &RenamedPlugin{
			ServicePlugin: p,
			originalName:  originalName,
			renamedName:   finalName,
		}
	}

	mfs.pluginFactories[finalName] = func() plugin.ServicePlugin {
		return pluginToRegister
	}

	mfs.mu.Unlock()

	log.Infof("Registered external plugin factory: %s", finalName)

	if renamed {
		return &RenamedPlugin{
			ServicePlugin: p,
			originalName:  originalName,
			renamedName:   finalName,
		}, nil
	}

	return p, nil
}

// UnloadExternalPluginWithType unloads an external plugin with an explicitly specified type
func (mfs *MountableFS) UnloadExternalPluginWithType(libraryPath string, pluginType loader.PluginType) error {
	return mfs.pluginLoader.UnloadPluginWithType(libraryPath, pluginType)
}

// UnloadExternalPlugin unloads an external plugin
func (mfs *MountableFS) UnloadExternalPlugin(libraryPath string) error {
	return mfs.pluginLoader.UnloadPlugin(libraryPath)
}

// GetLoadedExternalPlugins returns a list of loaded external plugin paths
func (mfs *MountableFS) GetLoadedExternalPlugins() []string {
	return mfs.pluginLoader.GetLoadedPlugins()
}

// GetPluginNameToPathMap returns a map of plugin names to their library paths
func (mfs *MountableFS) GetPluginNameToPathMap() map[string]string {
	return mfs.pluginLoader.GetPluginNameToPathMap()
}

// GetBuiltinPluginNames returns a list of all registered builtin plugin names
func (mfs *MountableFS) GetBuiltinPluginNames() []string {
	mfs.mu.RLock()
	defer mfs.mu.RUnlock()

	externalPlugins := mfs.pluginLoader.GetPluginNameToPathMap()

	names := make([]string, 0)
	for name := range mfs.pluginFactories {
		if _, isExternal := externalPlugins[name]; !isExternal {
			names = append(names, name)
		}
	}
	return names
}

// LoadExternalPluginsFromDirectory loads all plugins from a directory
func (mfs *MountableFS) LoadExternalPluginsFromDirectory(dir string) ([]string, []error) {
	return mfs.pluginLoader.LoadPluginsFromDirectory(dir)
}

// GetMounts returns all mount points
func (mfs *MountableFS) GetMounts() []*MountPoint {
	// Lock-free read
	tree := mfs.mountTree.Load().(*iradix.Tree)

	var mounts []*MountPoint
	tree.Root().Walk(func(k []byte, v interface{}) bool {
		mounts = append(mounts, v.(*MountPoint))
		return false
	})
	return mounts
}

// findMount finds the mount point for a given path using lock-free radix tree lookup
// Returns the mount and the relative path within the mount
func (mfs *MountableFS) findMount(path string) (*MountPoint, string, bool) {
	path = filesystem.NormalizePath(path)

	// Lock-free read
	tree := mfs.mountTree.Load().(*iradix.Tree)

	// LongestPrefix match
	k, v, found := tree.Root().LongestPrefix([]byte(path))
	if !found {
		return nil, "", false
	}

	mountPath := string(k)

	// Verify prefix boundary to ensure we don't match "/mnt-foo" against "/mnt"
	// 1. Exact match
	if len(path) == len(mountPath) {
		mount := v.(*MountPoint)
		return mount, "/", true
	}

	// 2. Subdirectory match (path must start with mountPath + "/")
	// Case A: mountPath is "/" -> path matches "/..." which is correct
	if mountPath == "/" {
		mount := v.(*MountPoint)
		return mount, path, true
	}

	// Case B: mountPath is "/mnt" -> path must be "/mnt/..."
	if len(path) > len(mountPath) && path[len(mountPath)] == '/' {
		mount := v.(*MountPoint)
		relPath := path[len(mountPath):]
		return mount, relPath, true
	}

	// Partial match failed (e.g. "/mnt-foo" matched "/mnt")
	return nil, "", false
}

// Delegate all FileSystem methods to either base FS or mounted plugin

func (mfs *MountableFS) Create(path string) error {
	mount, relPath, found := mfs.findMount(path)

	if found {
		return mount.Plugin.GetFileSystem().Create(relPath)
	}
	return filesystem.NewPermissionDeniedError("create", path, "not allowed to create file in rootfs, use mount instead")
}

func (mfs *MountableFS) Mkdir(path string, perm uint32) error {
	mount, relPath, found := mfs.findMount(path)

	if found {
		return mount.Plugin.GetFileSystem().Mkdir(relPath, perm)
	}
	return filesystem.NewPermissionDeniedError("mkdir", path, "not allowed to create directory in rootfs, use mount instead")
}

func (mfs *MountableFS) Remove(path string) error {
	mount, relPath, found := mfs.findMount(path)

	if found {
		return mount.Plugin.GetFileSystem().Remove(relPath)
	}
	return filesystem.NewNotFoundError("remove", path)
}

func (mfs *MountableFS) RemoveAll(path string) error {
	mount, relPath, found := mfs.findMount(path)

	if found {
		return mount.Plugin.GetFileSystem().RemoveAll(relPath)
	}
	return filesystem.NewNotFoundError("removeall", path)
}

func (mfs *MountableFS) Read(path string, offset int64, size int64) ([]byte, error) {
	mount, relPath, found := mfs.findMount(path)

	if found {
		return mount.Plugin.GetFileSystem().Read(relPath, offset, size)
	}
	return nil, filesystem.NewNotFoundError("read", path)
}

func (mfs *MountableFS) Write(path string, data []byte, offset int64, flags filesystem.WriteFlag) (int64, error) {
	mount, relPath, found := mfs.findMount(path)

	if found {
		return mount.Plugin.GetFileSystem().Write(relPath, data, offset, flags)
	}
	return 0, filesystem.NewNotFoundError("write", path)
}

func (mfs *MountableFS) ReadDir(path string) ([]filesystem.FileInfo, error) {
	// Lock-free implementation
	path = filesystem.NormalizePath(path)

	// 1. Check if we are listing a directory inside a mount
	mount, relPath, found := mfs.findMount(path)
	if found {
		// Get contents from the mounted filesystem
		infos, err := mount.Plugin.GetFileSystem().ReadDir(relPath)
		if err != nil {
			return nil, err
		}

		// Also check for any nested mounts directly under this path
		// e.g. mounted at /mnt, and we have /mnt/foo mounted
		tree := mfs.mountTree.Load().(*iradix.Tree)
		
		// We want to find all mounts that are strictly children of `path`
		// e.g. path="/mnt", mount="/mnt/foo" -> prefix match "/mnt/"
		prefix := path
		if !strings.HasSuffix(prefix, "/") {
			prefix += "/"
		}

		// Walk prefix to find direct children
		tree.Root().WalkPrefix([]byte(prefix), func(k []byte, v interface{}) bool {
			mountPath := string(k)
			// Only show direct children
			// e.g. prefix="/mnt/", mountPath="/mnt/foo" -> OK
			// mountPath="/mnt/foo/bar" -> SKIP (will be shown when listing /mnt/foo)
			
			rel := strings.TrimPrefix(mountPath, prefix)
			if !strings.Contains(rel, "/") && rel != "" {
				// Avoid duplicates if the plugin already reported it
				exists := false
				for _, info := range infos {
					if info.Name == rel {
						exists = true
						break
					}
				}
				if !exists {
					infos = append(infos, filesystem.FileInfo{
						Name:    rel,
						Size:    0,
						Mode:    0755,
						ModTime: time.Now(),
						IsDir:   true,
						Meta: filesystem.MetaData{
							Type: MetaValueMountPoint,
						},
					})
				}
			}
			return false
		})

		return infos, nil
	}

	// 2. We are not in a mount, so we are listing the virtual root or intermediate directories
	tree := mfs.mountTree.Load().(*iradix.Tree)
	var infos []filesystem.FileInfo
	seenDirs := make(map[string]bool)

	prefix := path
	if !strings.HasSuffix(prefix, "/") {
		prefix += "/"
	}

	// Walk all keys that have this prefix
	tree.Root().WalkPrefix([]byte(prefix), func(k []byte, v interface{}) bool {
		mountPath := string(k)
		
		// Extract the next directory component
		rel := strings.TrimPrefix(mountPath, prefix)
		if rel == "" {
			return false // Should not happen if path logic is correct (path is not a mount)
		}

		parts := strings.SplitN(rel, "/", 2)
		nextDir := parts[0]

		if !seenDirs[nextDir] {
			seenDirs[nextDir] = true
			infos = append(infos, filesystem.FileInfo{
				Name:    nextDir,
				Size:    0,
				Mode:    0755,
				ModTime: time.Now(),
				IsDir:   true,
				Meta: filesystem.MetaData{
					Name: "rootfs",
					Type: MetaValueMountPoint,
				},
			})
		}
		return false 
	})

	if len(infos) > 0 {
		return infos, nil
	}

	return nil, filesystem.NewNotFoundError("readdir", path)
}

func (mfs *MountableFS) Stat(path string) (*filesystem.FileInfo, error) {
	path = filesystem.NormalizePath(path)

	// Check if path is root
	if path == "/" {
		return &filesystem.FileInfo{
			Name:    "/",
			Size:    0,
			Mode:    0755,
			ModTime: time.Now(),
			IsDir:   true,
			Meta: filesystem.MetaData{
				Type: MetaValueRoot,
			},
		}, nil
	}

	// Check if path is a mount point or within a mount
	mount, relPath, found := mfs.findMount(path)
	if found {
		stat, err := mount.Plugin.GetFileSystem().Stat(relPath)
		if err != nil {
			return nil, err
		}

		// Fix name if querying the mount point itself
		if path == mount.Path && stat.Name == "/" {
			name := path[1:]
			if lastSlash := strings.LastIndex(name, "/"); lastSlash >= 0 {
				name = name[lastSlash+1:]
			}
			if name == "" {
				name = "/"
			}
			stat.Name = name
		}

		return stat, nil
	}

	// Check if path is a parent directory of any mount points
	// e.g. /mnt when /mnt/foo exists
	tree := mfs.mountTree.Load().(*iradix.Tree)
	prefix := path
	if !strings.HasSuffix(prefix, "/") {
		prefix += "/"
	}
	
	isParent := false
	tree.Root().WalkPrefix([]byte(prefix), func(k []byte, v interface{}) bool {
		isParent = true
		return true // Stop iteration
	})

	if isParent {
		name := path[1:]
		if lastSlash := strings.LastIndex(name, "/"); lastSlash >= 0 {
			name = name[lastSlash+1:]
		}
		if name == "" {
			name = "/"
		}
		return &filesystem.FileInfo{
			Name:    name,
			Size:    0,
			Mode:    0755,
			ModTime: time.Now(),
			IsDir:   true,
			Meta: filesystem.MetaData{
				Type: MetaValueMountPoint,
			},
		}, nil
	}

	return nil, filesystem.NewNotFoundError("stat", path)
}

func (mfs *MountableFS) Rename(oldPath, newPath string) error {
	// findMount is now lock-free
	oldMount, oldRelPath, oldFound := mfs.findMount(oldPath)
	newMount, newRelPath, newFound := mfs.findMount(newPath)

	if oldFound && newFound {
		if oldMount != newMount {
			return fmt.Errorf("cannot rename across different mounts")
		}
		return oldMount.Plugin.GetFileSystem().Rename(oldRelPath, newRelPath)
	}

	return fmt.Errorf("cannot rename: paths not in same mounted filesystem")
}

func (mfs *MountableFS) Chmod(path string, mode uint32) error {
	mount, relPath, found := mfs.findMount(path)

	if found {
		return mount.Plugin.GetFileSystem().Chmod(relPath, mode)
	}
	return filesystem.NewNotFoundError("chmod", path)
}

// Touch implements filesystem.Toucher interface
func (mfs *MountableFS) Touch(path string) error {
	mount, relPath, found := mfs.findMount(path)

	if found {
		fs := mount.Plugin.GetFileSystem()
		if toucher, ok := fs.(filesystem.Toucher); ok {
			return toucher.Touch(relPath)
		}
		info, err := fs.Stat(relPath)
		if err == nil {
			if !info.IsDir {
				data, readErr := fs.Read(relPath, 0, -1)
				if readErr != nil {
					return readErr
				}
				_, writeErr := fs.Write(relPath, data, -1, filesystem.WriteFlagNone)
				return writeErr
			}
			return fmt.Errorf("cannot touch directory")
		} else {
			_, err := fs.Write(relPath, []byte{}, -1, filesystem.WriteFlagCreate)
			return err
		}
	}
	return filesystem.NewNotFoundError("touch", path)
}

func (mfs *MountableFS) Open(path string) (io.ReadCloser, error) {
	mount, relPath, found := mfs.findMount(path)

	if found {
		return mount.Plugin.GetFileSystem().Open(relPath)
	}
	return nil, filesystem.NewNotFoundError("open", path)
}

func (mfs *MountableFS) OpenWrite(path string) (io.WriteCloser, error) {
	mount, relPath, found := mfs.findMount(path)

	if found {
		return mount.Plugin.GetFileSystem().OpenWrite(relPath)
	}
	return nil, filesystem.NewNotFoundError("openwrite", path)
}

// OpenStream implements filesystem.Streamer interface
func (mfs *MountableFS) OpenStream(path string) (filesystem.StreamReader, error) {
	mount, relPath, found := mfs.findMount(path)

	if !found {
		return nil, filesystem.NewNotFoundError("openstream", path)
	}

	fs := mount.Plugin.GetFileSystem()
	if streamer, ok := fs.(filesystem.Streamer); ok {
		log.Debugf("[mountablefs] OpenStream: found streamer for path %s (relPath: %s, fs type: %T)", path, relPath, fs)
		return streamer.OpenStream(relPath)
	}

	log.Debugf("[mountablefs] OpenStream: filesystem does not support streaming: %s (fs type: %T)", path, fs)
	return nil, fmt.Errorf("filesystem does not support streaming: %s", path)
}

// GetStream tries to get a stream from the underlying filesystem if it supports streaming
// Deprecated: Use OpenStream instead
func (mfs *MountableFS) GetStream(path string) (interface{}, error) {
	mount, relPath, found := mfs.findMount(path)

	if !found {
		return nil, filesystem.NewNotFoundError("getstream", path)
	}

	type streamGetter interface {
		GetStream(path string) (interface{}, error)
	}

	fs := mount.Plugin.GetFileSystem()
	if sg, ok := fs.(streamGetter); ok {
		log.Debugf("[mountablefs] GetStream: found stream getter for path %s (relPath: %s, fs type: %T)", path, relPath, fs)
		return sg.GetStream(relPath)
	}

	log.Warnf("[mountablefs] GetStream: filesystem does not support streaming: %s (fs type: %T)", path, fs)
	return nil, fmt.Errorf("filesystem does not support streaming: %s", path)
}

// ============================================================================
// HandleFS Implementation
// ============================================================================

// OpenHandle opens a file and returns a handle for stateful operations
// This delegates to the underlying filesystem if it supports HandleFS
func (mfs *MountableFS) OpenHandle(path string, flags filesystem.OpenFlag, mode uint32) (filesystem.FileHandle, error) {
	mount, relPath, found := mfs.findMount(path)

	if !found {
		return nil, filesystem.NewNotFoundError("openhandle", path)
	}

	fs := mount.Plugin.GetFileSystem()
	handleFS, ok := fs.(filesystem.HandleFS)
	if !ok {
		return nil, filesystem.NewNotSupportedError("openhandle", path)
	}

	// Open handle in the underlying filesystem
	localHandle, err := handleFS.OpenHandle(relPath, flags, mode)
	if err != nil {
		return nil, err
	}

	// Generate a globally unique handle ID
	globalID := mfs.globalHandleID.Add(1)

	// Store the mapping: globalID -> (mount, localHandle)
	mfs.handleInfosMu.Lock()
	mfs.handleInfos[globalID] = &handleInfo{
		mount:       mount,
		localHandle: localHandle,
	}
	mfs.handleInfosMu.Unlock()

	// Return a wrapper that uses the global ID
	return &globalFileHandle{
		globalID:    globalID,
		localHandle: localHandle,
		mountPath:   mount.Path,
		fullPath:    path,
	}, nil
}

// GetHandle retrieves an existing handle by its ID
func (mfs *MountableFS) GetHandle(id int64) (filesystem.FileHandle, error) {
	// Look up the handle info using the global ID
	mfs.handleInfosMu.RLock()
	info, found := mfs.handleInfos[id]
	mfs.handleInfosMu.RUnlock()

	if !found {
		return nil, filesystem.ErrNotFound
	}

	// Return a wrapper with the global ID
	return &globalFileHandle{
		globalID:    id,
		localHandle: info.localHandle,
		mountPath:   info.mount.Path,
		fullPath:    info.mount.Path + info.localHandle.Path(),
	}, nil
}

// CloseHandle closes a handle by its ID
func (mfs *MountableFS) CloseHandle(id int64) error {
	// Look up the handle info using the global ID
	mfs.handleInfosMu.RLock()
	info, found := mfs.handleInfos[id]
	mfs.handleInfosMu.RUnlock()

	if !found {
		return filesystem.ErrNotFound
	}

	// Close the underlying local handle
	err := info.localHandle.Close()
	if err == nil {
		// Remove from mapping
		mfs.handleInfosMu.Lock()
		delete(mfs.handleInfos, id)
		mfs.handleInfosMu.Unlock()
	}

	return err
}

// globalFileHandle wraps a local file handle with a globally unique ID
// This prevents handle ID conflicts when multiple plugin instances are mounted
type globalFileHandle struct {
	globalID    int64                 // Globally unique ID assigned by MountableFS
	localHandle filesystem.FileHandle // Underlying handle from the plugin
	mountPath   string                // Mount path for this handle
	fullPath    string                // Full path including mount point
}

// ID returns the globally unique handle ID
func (h *globalFileHandle) ID() int64 {
	return h.globalID
}

// Path returns the full path (including mount point)
func (h *globalFileHandle) Path() string {
	return h.fullPath
}

// Read delegates to the underlying handle
func (h *globalFileHandle) Read(buf []byte) (int, error) {
	return h.localHandle.Read(buf)
}

// ReadAt delegates to the underlying handle
func (h *globalFileHandle) ReadAt(buf []byte, offset int64) (int, error) {
	return h.localHandle.ReadAt(buf, offset)
}

// Write delegates to the underlying handle
func (h *globalFileHandle) Write(data []byte) (int, error) {
	return h.localHandle.Write(data)
}

// WriteAt delegates to the underlying handle
func (h *globalFileHandle) WriteAt(data []byte, offset int64) (int, error) {
	return h.localHandle.WriteAt(data, offset)
}

// Seek delegates to the underlying handle
func (h *globalFileHandle) Seek(offset int64, whence int) (int64, error) {
	return h.localHandle.Seek(offset, whence)
}

// Sync delegates to the underlying handle
func (h *globalFileHandle) Sync() error {
	return h.localHandle.Sync()
}

// Close delegates to the underlying handle
func (h *globalFileHandle) Close() error {
	return h.localHandle.Close()
}

// Stat delegates to the underlying handle
func (h *globalFileHandle) Stat() (*filesystem.FileInfo, error) {
	return h.localHandle.Stat()
}

// Flags delegates to the underlying handle
func (h *globalFileHandle) Flags() filesystem.OpenFlag {
	return h.localHandle.Flags()
}

// Ensure MountableFS implements HandleFS interface
var _ filesystem.HandleFS = (*MountableFS)(nil)
var _ filesystem.FileHandle = (*globalFileHandle)(nil)