"""
NFS Userspace Client - Full POSIX File Lock Support

NOTE: NFS support has been DISABLED due to libnfs C library compatibility issues.
Use WebDAV backend instead (set IEOPS_WEBDAV_URL environment variable).

Using ctypes to call libnfs C library, supports:
- Basic file operations (read, write, create, delete, etc.)
- NFSv4 file locks (lockf, fcntl style)
- Thread safety
- Context manager

To re-enable NFS support:
1. Remove the 'raise ImportError' line below
2. Change 'if False:' to 'if True:' or remove the if block
3. Rebuild libnfs in Dockerfile (uncomment libnfs build steps)
4. Uncomment NFS references in __init__.py and filesystem.py
"""

# ============================================================================
# NFS MODULE DISABLED
# ============================================================================

raise ImportError(
    "NFS support has been disabled. "
    "Use WebDAV backend instead (set IEOPS_WEBDAV_URL environment variable)."
)

# ============================================================================
# ORIGINAL NFS CODE - COMMENTED OUT (preserved for future re-enablement)
# ============================================================================
# The code below is wrapped in 'if False:' to disable it while preserving
# the original implementation. To re-enable, change to 'if True:' and
# remove the 'raise ImportError' above.

if False:  # NFS DISABLED - change to True to re-enable
    import ctypes
    import ctypes.util
    import os
    import threading
    from contextlib import contextmanager
    from typing import Optional, Union, List, Generator
    from dataclasses import dataclass
    from enum import IntEnum
    
    
    # ============================================================================
    # libnfs C Library Loading and Type Definitions
    # ============================================================================
    
    def _load_libnfs():
        """Load libnfs shared library (prefer 6.x for port support)"""
        import glob
        
        # Try multiple possible library names (prefer 6.x for nfs_set_nfsport support)
        lib_names = [
            "/usr/local/lib/libnfs.so",          # local install symlink (preferred)
            "libnfs.so.16",                      # libnfs 6.x
            "libnfs.so.14",                      # libnfs 5.x
            "libnfs.so.13", 
            "libnfs.so",
            "nfs",
        ]
        
        # Also try to find versioned .so files via glob (e.g., libnfs.so.16.2.0, libnfs.so.16.3.0)
        versioned_libs = sorted(glob.glob("/usr/local/lib/libnfs.so.16.*"), reverse=True)
        lib_names = versioned_libs + lib_names
        
        for name in lib_names:
            try:
                if name == "nfs":
                    path = ctypes.util.find_library(name)
                    if path:
                        return ctypes.CDLL(path, mode=ctypes.RTLD_GLOBAL)
                else:
                    return ctypes.CDLL(name, mode=ctypes.RTLD_GLOBAL)
            except OSError:
                continue
        
        raise OSError("Failed to load libnfs library. Please install libnfs >= 6.0: apt-get install libnfs-dev or build from source")
    
    
    # Load library
    _libnfs = _load_libnfs()
    
    # C type definitions
    c_void_p = ctypes.c_void_p
    c_char_p = ctypes.c_char_p
    c_int = ctypes.c_int
    c_int64 = ctypes.c_int64
    c_uint64 = ctypes.c_uint64
    c_size_t = ctypes.c_size_t
    
    
    # ============================================================================
    # Lock Operation Constants
    # ============================================================================
    
    class LockOp(IntEnum):
        """lockf style lock operations"""
        F_LOCK = 0   # Acquire lock (blocking)
        F_TLOCK = 1  # Try to acquire lock (non-blocking)
        F_ULOCK = 2  # Release lock
        F_TEST = 3   # Test lock
    
    
    class FcntlOp(IntEnum):
        """fcntl style lock operations"""
        F_SETLK = 0   # Non-blocking set lock
        F_SETLKW = 1  # Blocking set lock
    
    
    class LockType(IntEnum):
        """Lock types"""
        F_RDLCK = 0  # Read lock (shared)
        F_WRLCK = 1  # Write lock (exclusive)
        F_UNLCK = 2  # Unlock
    
    
    class Whence(IntEnum):
        """seek origin position"""
        SEEK_SET = 0
        SEEK_CUR = 1
        SEEK_END = 2
    
    
    # ============================================================================
    # fcntl Lock Structure
    # ============================================================================
    
    class NFSFlock(ctypes.Structure):
        """NFS fcntl lock structure"""
        _fields_ = [
            ("l_type", c_int),      # F_RDLCK, F_WRLCK, F_UNLCK
            ("l_whence", c_int),    # SEEK_SET, SEEK_CUR, SEEK_END
            ("l_start", c_uint64),  # Lock start position
            ("l_len", c_uint64),    # Lock length, 0 means to end of file
            ("l_pid", c_int),       # PID of lock holder (only for F_GETLK)
        ]
    
    
    # ============================================================================
    # Define libnfs C Function Signatures
    # ============================================================================
    
    # Context management
    _libnfs.nfs_init_context.argtypes = []
    _libnfs.nfs_init_context.restype = c_void_p
    
    _libnfs.nfs_destroy_context.argtypes = [c_void_p]
    _libnfs.nfs_destroy_context.restype = None
    
    _libnfs.nfs_get_error.argtypes = [c_void_p]
    _libnfs.nfs_get_error.restype = c_char_p
    
    # Timeout configuration
    _libnfs.nfs_set_timeout.argtypes = [c_void_p, c_int]
    _libnfs.nfs_set_timeout.restype = None
    
    # Port configuration (libnfs 6.x+)
    try:
        _libnfs.nfs_set_nfsport.argtypes = [c_void_p, c_int]
        _libnfs.nfs_set_nfsport.restype = None
        _HAS_SET_NFSPORT = True
    except AttributeError:
        _HAS_SET_NFSPORT = False
    
    # Default timeout in milliseconds (will be overridden by config)
    _DEFAULT_TIMEOUT_MS = 30000
    
    # Mount
    _libnfs.nfs_mount.argtypes = [c_void_p, c_char_p, c_char_p]
    _libnfs.nfs_mount.restype = c_int
    
    # File operations
    _libnfs.nfs_open.argtypes = [c_void_p, c_char_p, c_int, ctypes.POINTER(c_void_p)]
    _libnfs.nfs_open.restype = c_int
    
    _libnfs.nfs_close.argtypes = [c_void_p, c_void_p]
    _libnfs.nfs_close.restype = c_int
    
    _libnfs.nfs_creat.argtypes = [c_void_p, c_char_p, c_int, ctypes.POINTER(c_void_p)]
    _libnfs.nfs_creat.restype = c_int
    
    # nfs_read(ctx, fh, buf, count) - note: buf before count!
    _libnfs.nfs_read.argtypes = [c_void_p, c_void_p, c_char_p, c_uint64]
    _libnfs.nfs_read.restype = c_int
    
    # nfs_write(ctx, fh, buf, count) - note: buf before count!
    _libnfs.nfs_write.argtypes = [c_void_p, c_void_p, c_char_p, c_uint64]
    _libnfs.nfs_write.restype = c_int
    
    _libnfs.nfs_lseek.argtypes = [c_void_p, c_void_p, c_int64, c_int, ctypes.POINTER(c_uint64)]
    _libnfs.nfs_lseek.restype = c_int
    
    _libnfs.nfs_ftruncate.argtypes = [c_void_p, c_void_p, c_uint64]
    _libnfs.nfs_ftruncate.restype = c_int
    
    _libnfs.nfs_fsync.argtypes = [c_void_p, c_void_p]
    _libnfs.nfs_fsync.restype = c_int
    
    # Lock operations
    _libnfs.nfs_lockf.argtypes = [c_void_p, c_void_p, c_int, c_uint64]
    _libnfs.nfs_lockf.restype = c_int
    
    _libnfs.nfs_fcntl.argtypes = [c_void_p, c_void_p, c_int, c_void_p]
    _libnfs.nfs_fcntl.restype = c_int
    
    # Directory operations
    _libnfs.nfs_mkdir.argtypes = [c_void_p, c_char_p]
    _libnfs.nfs_mkdir.restype = c_int
    
    _libnfs.nfs_rmdir.argtypes = [c_void_p, c_char_p]
    _libnfs.nfs_rmdir.restype = c_int
    
    _libnfs.nfs_opendir.argtypes = [c_void_p, c_char_p, ctypes.POINTER(c_void_p)]
    _libnfs.nfs_opendir.restype = c_int
    
    _libnfs.nfs_closedir.argtypes = [c_void_p, c_void_p]
    _libnfs.nfs_closedir.restype = None
    
    # File management
    _libnfs.nfs_unlink.argtypes = [c_void_p, c_char_p]
    _libnfs.nfs_unlink.restype = c_int
    
    _libnfs.nfs_rename.argtypes = [c_void_p, c_char_p, c_char_p]
    _libnfs.nfs_rename.restype = c_int
    
    _libnfs.nfs_symlink.argtypes = [c_void_p, c_char_p, c_char_p]
    _libnfs.nfs_symlink.restype = c_int
    
    _libnfs.nfs_link.argtypes = [c_void_p, c_char_p, c_char_p]
    _libnfs.nfs_link.restype = c_int
    
    _libnfs.nfs_chmod.argtypes = [c_void_p, c_char_p, c_int]
    _libnfs.nfs_chmod.restype = c_int
    
    _libnfs.nfs_chown.argtypes = [c_void_p, c_char_p, c_int, c_int]
    _libnfs.nfs_chown.restype = c_int
    
    _libnfs.nfs_truncate.argtypes = [c_void_p, c_char_p, c_uint64]
    _libnfs.nfs_truncate.restype = c_int
    
    
    # ============================================================================
    # nfs_stat64 Structure
    # ============================================================================
    
    class NFSStat64(ctypes.Structure):
        """NFS file status structure"""
        _fields_ = [
            ("nfs_dev", c_uint64),
            ("nfs_ino", c_uint64),
            ("nfs_mode", c_uint64),
            ("nfs_nlink", c_uint64),
            ("nfs_uid", c_uint64),
            ("nfs_gid", c_uint64),
            ("nfs_rdev", c_uint64),
            ("nfs_size", c_uint64),
            ("nfs_blksize", c_uint64),
            ("nfs_blocks", c_uint64),
            ("nfs_atime", c_uint64),
            ("nfs_mtime", c_uint64),
            ("nfs_ctime", c_uint64),
            ("nfs_atime_nsec", c_uint64),
            ("nfs_mtime_nsec", c_uint64),
            ("nfs_ctime_nsec", c_uint64),
            ("nfs_used", c_uint64),
        ]
    
    
    _libnfs.nfs_stat64.argtypes = [c_void_p, c_char_p, ctypes.POINTER(NFSStat64)]
    _libnfs.nfs_stat64.restype = c_int
    
    _libnfs.nfs_fstat64.argtypes = [c_void_p, c_void_p, ctypes.POINTER(NFSStat64)]
    _libnfs.nfs_fstat64.restype = c_int
    
    _libnfs.nfs_lstat64.argtypes = [c_void_p, c_char_p, ctypes.POINTER(NFSStat64)]
    _libnfs.nfs_lstat64.restype = c_int
    
    
    # ============================================================================
    # nfsdirent Structure (for directory traversal)
    # ============================================================================
    
    class NFSDirent(ctypes.Structure):
        """NFS directory entry structure"""
        pass
    
    NFSDirent._fields_ = [
        ("next", ctypes.POINTER(NFSDirent)),
        ("name", c_char_p),
        ("inode", c_uint64),
        ("type", c_int),
        ("mode", c_int),
        ("size", c_uint64),
        ("atime", c_uint64),
        ("mtime", c_uint64),
        ("ctime", c_uint64),
        ("uid", c_uint64),
        ("gid", c_uint64),
        ("nlink", c_uint64),
        ("dev", c_uint64),
        ("rdev", c_uint64),
        ("blksize", c_uint64),
        ("blocks", c_uint64),
        ("used", c_uint64),
        ("atime_nsec", c_uint64),
        ("mtime_nsec", c_uint64),
        ("ctime_nsec", c_uint64),
    ]
    
    _libnfs.nfs_readdir.argtypes = [c_void_p, c_void_p]
    _libnfs.nfs_readdir.restype = ctypes.POINTER(NFSDirent)
    
    
    # ============================================================================
    # Exception Classes
    # ============================================================================
    
    class NFSError(Exception):
        """NFS operation error"""
        def __init__(self, message: str, errno: int = 0):
            self.errno = errno
            super().__init__(message)
    
    
    class NFSLockError(NFSError):
        """NFS lock error"""
        pass
    
    
    # ============================================================================
    # Data Classes
    # ============================================================================
    
    @dataclass
    class FileStat:
        """File status information"""
        size: int
        mode: int
        uid: int
        gid: int
        atime: int
        mtime: int
        ctime: int
        nlink: int
        ino: int
        dev: int
        blocks: int
        blksize: int
        
        @property
        def is_dir(self) -> bool:
            import stat
            return stat.S_ISDIR(self.mode)
        
        @property
        def is_file(self) -> bool:
            import stat
            return stat.S_ISREG(self.mode)
        
        @property
        def is_link(self) -> bool:
            import stat
            return stat.S_ISLNK(self.mode)
    
    
    @dataclass
    class DirEntry:
        """Directory entry"""
        name: str
        inode: int
        type: int
        mode: int
        size: int
    
    
    # ============================================================================
    # NFS Context Class (Low-level)
    # ============================================================================
    
    class NFSContext:
        """NFS connection context (low-level wrapper)"""
        
        def __init__(self, server: str, export: str, port: Optional[int] = None, timeout_ms: Optional[int] = None):
            """
            Initialize NFS connection
            
            Args:
                server: NFS server address
                export: Export path
                port: NFS port (optional, default 2049, requires libnfs 6.x+)
                timeout_ms: Connection timeout in milliseconds (optional, uses config default)
            """
            self._ctx = _libnfs.nfs_init_context()
            if not self._ctx:
                raise NFSError("Failed to initialize NFS context")
            
            self._server = server
            self._export = export
            self._port = port
            self._mounted = False
            self._lock = threading.Lock()
            
            # Set connection timeout from config or parameter
            from ..config import env
            actual_timeout = timeout_ms if timeout_ms is not None else env.storage.timeout
            _libnfs.nfs_set_timeout(self._ctx, actual_timeout)
            
            # Set custom NFS port if specified (requires libnfs 6.x+)
            if port is not None and port != 2049:
                if _HAS_SET_NFSPORT:
                    _libnfs.nfs_set_nfsport(self._ctx, port)
                else:
                    _libnfs.nfs_destroy_context(self._ctx)
                    raise NFSError(f"Custom port {port} requires libnfs 6.x+. Current version doesn't support nfs_set_nfsport()")
            
            # Mount
            ret = _libnfs.nfs_mount(
                self._ctx,
                server.encode('utf-8'),
                export.encode('utf-8')
            )
            if ret != 0:
                error = self._get_error()
                _libnfs.nfs_destroy_context(self._ctx)
                raise NFSError(f"NFS mount failed: {error}", ret)
            
            self._mounted = True
        
        def _get_error(self) -> str:
            """Get last error message"""
            err = _libnfs.nfs_get_error(self._ctx)
            if err:
                return err.decode('utf-8', errors='replace')
            return "Unknown error"
        
        def _check_ret(self, ret: int, operation: str):
            """Check return value, raise exception on failure"""
            if ret < 0:
                raise NFSError(f"{operation} failed: {self._get_error()}", ret)
        
        def close(self):
            """Close connection"""
            if self._ctx:
                _libnfs.nfs_destroy_context(self._ctx)
                self._ctx = None
                self._mounted = False
        
        def __del__(self):
            self.close()
        
        # ============ File Operations ============
        
        def open(self, path: str, flags: int) -> c_void_p:
            """Open file, return file handle"""
            fh = c_void_p()
            ret = _libnfs.nfs_open(
                self._ctx,
                path.encode('utf-8'),
                flags,
                ctypes.byref(fh)
            )
            self._check_ret(ret, f"Open file {path}")
            return fh
        
        def creat(self, path: str, mode: int = 0o644) -> c_void_p:
            """Create file, return file handle"""
            fh = c_void_p()
            ret = _libnfs.nfs_creat(
                self._ctx,
                path.encode('utf-8'),
                mode,
                ctypes.byref(fh)
            )
            self._check_ret(ret, f"Create file {path}")
            return fh
        
        def close_file(self, fh: c_void_p):
            """Close file"""
            ret = _libnfs.nfs_close(self._ctx, fh)
            self._check_ret(ret, "Close file")
        
        def read(self, fh: c_void_p, count: int) -> bytes:
            """Read data"""
            buf = ctypes.create_string_buffer(count)
            ret = _libnfs.nfs_read(self._ctx, fh, buf, count)
            self._check_ret(ret, "Read file")
            return buf.raw[:ret]
        
        def write(self, fh: c_void_p, data: bytes) -> int:
            """Write data, return bytes written"""
            ret = _libnfs.nfs_write(self._ctx, fh, data, len(data))
            self._check_ret(ret, "Write file")
            return ret
        
        def lseek(self, fh: c_void_p, offset: int, whence: int) -> int:
            """Move file pointer, return new position"""
            new_offset = c_uint64()
            ret = _libnfs.nfs_lseek(self._ctx, fh, offset, whence, ctypes.byref(new_offset))
            self._check_ret(ret, "Seek file")
            return new_offset.value
        
        def ftruncate(self, fh: c_void_p, length: int):
            """Truncate file"""
            ret = _libnfs.nfs_ftruncate(self._ctx, fh, length)
            self._check_ret(ret, "Truncate file")
        
        def fsync(self, fh: c_void_p):
            """Sync file to disk"""
            ret = _libnfs.nfs_fsync(self._ctx, fh)
            self._check_ret(ret, "Sync file")
        
        def fstat(self, fh: c_void_p) -> FileStat:
            """Get file status (via handle)"""
            st = NFSStat64()
            ret = _libnfs.nfs_fstat64(self._ctx, fh, ctypes.byref(st))
            self._check_ret(ret, "Get file status")
            return self._stat_to_filestat(st)
        
        # ============ Lock Operations ============
        
        def lockf(self, fh: c_void_p, op: LockOp, count: int = 0) -> bool:
            """
            lockf style file lock
            
            Args:
                fh: File handle
                op: Lock operation (LockOp.F_LOCK, F_TLOCK, F_ULOCK, F_TEST)
                count: Bytes to lock, 0 means from current position to end of file
                
            Returns:
                True on success
                
            Raises:
                NFSLockError: On lock operation failure
            """
            ret = _libnfs.nfs_lockf(self._ctx, fh, int(op), count)
            if ret < 0:
                if op == LockOp.F_TLOCK and ret == -11:  # EAGAIN
                    return False
                raise NFSLockError(f"Lock operation failed: {self._get_error()}", ret)
            return True
        
        def fcntl_lock(self, fh: c_void_p, cmd: FcntlOp, 
                       lock_type: LockType, start: int = 0, 
                       length: int = 0, whence: Whence = Whence.SEEK_SET) -> bool:
            """
            fcntl style file lock (supports byte range locks)
            
            Args:
                fh: File handle
                cmd: Lock command (FcntlOp.F_SETLK non-blocking, F_SETLKW blocking)
                lock_type: Lock type (LockType.F_RDLCK, F_WRLCK, F_UNLCK)
                start: Lock start position
                length: Lock length, 0 means to end of file
                whence: Reference position for start
                
            Returns:
                True on success
                
            Raises:
                NFSLockError: On lock operation failure
            """
            flock = NFSFlock()
            flock.l_type = int(lock_type)
            flock.l_whence = int(whence)
            flock.l_start = start
            flock.l_len = length
            flock.l_pid = 0
            
            ret = _libnfs.nfs_fcntl(self._ctx, fh, int(cmd), ctypes.byref(flock))
            if ret < 0:
                if cmd == FcntlOp.F_SETLK and ret == -11:  # EAGAIN
                    return False
                raise NFSLockError(f"fcntl lock operation failed: {self._get_error()}", ret)
            return True
        
        # ============ Directory Operations ============
        
        def mkdir(self, path: str):
            """Create directory"""
            ret = _libnfs.nfs_mkdir(self._ctx, path.encode('utf-8'))
            self._check_ret(ret, f"Create directory {path}")
        
        def rmdir(self, path: str):
            """Remove directory"""
            ret = _libnfs.nfs_rmdir(self._ctx, path.encode('utf-8'))
            self._check_ret(ret, f"Remove directory {path}")
        
        def listdir(self, path: str) -> List[DirEntry]:
            """List directory contents"""
            dirh = c_void_p()
            ret = _libnfs.nfs_opendir(self._ctx, path.encode('utf-8'), ctypes.byref(dirh))
            self._check_ret(ret, f"Open directory {path}")
            
            entries = []
            try:
                while True:
                    dirent = _libnfs.nfs_readdir(self._ctx, dirh)
                    if not dirent:
                        break
                    d = dirent.contents
                    name = d.name.decode('utf-8') if d.name else ""
                    if name and name not in ('.', '..'):
                        entries.append(DirEntry(
                            name=name,
                            inode=d.inode,
                            type=d.type,
                            mode=d.mode,
                            size=d.size
                        ))
            finally:
                _libnfs.nfs_closedir(self._ctx, dirh)
            
            return entries
        
        # ============ File Management ============
        
        def unlink(self, path: str):
            """Delete file"""
            ret = _libnfs.nfs_unlink(self._ctx, path.encode('utf-8'))
            self._check_ret(ret, f"Delete file {path}")
        
        def rename(self, old_path: str, new_path: str):
            """Rename file"""
            ret = _libnfs.nfs_rename(
                self._ctx,
                old_path.encode('utf-8'),
                new_path.encode('utf-8')
            )
            self._check_ret(ret, f"Rename {old_path} -> {new_path}")
        
        def stat(self, path: str) -> FileStat:
            """Get file status"""
            st = NFSStat64()
            ret = _libnfs.nfs_stat64(self._ctx, path.encode('utf-8'), ctypes.byref(st))
            self._check_ret(ret, f"Get file status {path}")
            return self._stat_to_filestat(st)
        
        def lstat(self, path: str) -> FileStat:
            """Get file status (do not follow symlinks)"""
            st = NFSStat64()
            ret = _libnfs.nfs_lstat64(self._ctx, path.encode('utf-8'), ctypes.byref(st))
            self._check_ret(ret, f"Get file status {path}")
            return self._stat_to_filestat(st)
        
        def chmod(self, path: str, mode: int):
            """Change file permissions"""
            ret = _libnfs.nfs_chmod(self._ctx, path.encode('utf-8'), mode)
            self._check_ret(ret, f"Change permissions {path}")
        
        def chown(self, path: str, uid: int, gid: int):
            """Change file owner"""
            ret = _libnfs.nfs_chown(self._ctx, path.encode('utf-8'), uid, gid)
            self._check_ret(ret, f"Change owner {path}")
        
        def truncate(self, path: str, length: int):
            """Truncate file"""
            ret = _libnfs.nfs_truncate(self._ctx, path.encode('utf-8'), length)
            self._check_ret(ret, f"Truncate file {path}")
        
        def symlink(self, target: str, link_path: str):
            """Create symbolic link"""
            ret = _libnfs.nfs_symlink(
                self._ctx,
                target.encode('utf-8'),
                link_path.encode('utf-8')
            )
            self._check_ret(ret, f"Create symlink {link_path}")
        
        def link(self, old_path: str, new_path: str):
            """Create hard link"""
            ret = _libnfs.nfs_link(
                self._ctx,
                old_path.encode('utf-8'),
                new_path.encode('utf-8')
            )
            self._check_ret(ret, f"Create hard link {new_path}")
        
        def _stat_to_filestat(self, st: NFSStat64) -> FileStat:
            """Convert NFSStat64 to FileStat"""
            return FileStat(
                size=st.nfs_size,
                mode=st.nfs_mode,
                uid=st.nfs_uid,
                gid=st.nfs_gid,
                atime=st.nfs_atime,
                mtime=st.nfs_mtime,
                ctime=st.nfs_ctime,
                nlink=st.nfs_nlink,
                ino=st.nfs_ino,
                dev=st.nfs_dev,
                blocks=st.nfs_blocks,
                blksize=st.nfs_blksize
            )
    
    
    # ============================================================================
    # NFS File Object (High-level Wrapper)
    # ============================================================================
    
    class NFSFile:
        """
        NFS File Object
        
        Simulates Python native file object, supports:
        - Read/write operations
        - seek/tell
        - File locks (lockf/fcntl style)
        - Context manager
        """
        
        def __init__(self, ctx: NFSContext, path: str, mode: str = 'r',
                     encoding: Optional[str] = None, errors: Optional[str] = None):
            """
            Open NFS file
            
            Args:
                ctx: NFS context
                path: File path
                mode: Open mode ('r', 'w', 'a', 'r+', 'w+', 'a+', with 'b' for binary)
                encoding: Text encoding (default: 'utf-8', ignored in binary mode)
                errors: Encoding error handling ('strict', 'ignore', 'replace', etc.)
            """
            self._ctx = ctx
            self._path = path
            self._mode = mode
            self._binary = 'b' in mode
            self._encoding = None if self._binary else (encoding or 'utf-8')
            self._errors = None if self._binary else (errors or 'strict')
            self._closed = False
            self._lock_held = False
            
            # Parse mode to flags
            flags = self._parse_mode(mode)
            needs_create = flags & os.O_CREAT
            
            # Open or create file
            if needs_create:
                # For write modes, use nfs_creat which properly handles file creation
                # First try to unlink existing file if O_TRUNC is set
                if flags & os.O_TRUNC:
                    try:
                        ctx.unlink(path)
                    except NFSError:
                        pass  # File doesn't exist, that's fine
                
                # Create file
                self._fh = ctx.creat(path, 0o644)
                
                # For read-write modes, close and reopen with correct flags
                if flags & os.O_RDWR:
                    ctx.close_file(self._fh)
                    self._fh = ctx.open(path, os.O_RDWR)
            else:
                self._fh = ctx.open(path, flags)
            
            # Append mode: move to end of file
            if 'a' in mode:
                ctx.lseek(self._fh, 0, Whence.SEEK_END)
        
        def _parse_mode(self, mode: str) -> int:
            """Parse mode string to open flags"""
            mode = mode.replace('b', '')
            
            if mode == 'r':
                return os.O_RDONLY
            elif mode == 'w':
                return os.O_WRONLY | os.O_CREAT | os.O_TRUNC
            elif mode == 'a':
                return os.O_WRONLY | os.O_CREAT | os.O_APPEND
            elif mode in ('r+', 'rw'):
                return os.O_RDWR
            elif mode == 'w+':
                return os.O_RDWR | os.O_CREAT | os.O_TRUNC
            elif mode == 'a+':
                return os.O_RDWR | os.O_CREAT | os.O_APPEND
            elif mode == 'x':
                return os.O_WRONLY | os.O_CREAT | os.O_EXCL
            elif mode == 'x+':
                return os.O_RDWR | os.O_CREAT | os.O_EXCL
            else:
                raise ValueError(f"Invalid mode: {mode}")
        
        @property
        def closed(self) -> bool:
            return self._closed
        
        @property
        def mode(self) -> str:
            return self._mode
        
        @property
        def name(self) -> str:
            return self._path
        
        @property
        def encoding(self) -> Optional[str]:
            return self._encoding
        
        @property
        def errors(self) -> Optional[str]:
            return self._errors
        
        def read(self, size: int = -1) -> Union[bytes, str]:
            """Read data (automatically chunks large reads)"""
            if self._closed:
                raise ValueError("I/O operation on closed file")
            
            if size == -1:
                # Read all
                st = self._ctx.fstat(self._fh)
                pos = self.tell()
                size = st.size - pos
            
            if size <= 0:
                return b'' if self._binary else ''
            
            # NFS has max request size limit, chunk large reads
            # Chunk size configurable via IEOPS_NFS_READ_CHUNK_SIZE env var
            from ..config import env
            chunk_size = env.storage.read_chunk_size
            
            if size <= chunk_size:
                # Small read, do it directly
                data = self._ctx.read(self._fh, size)
            else:
                # Large read, chunk it
                chunks = []
                remaining = size
                while remaining > 0:
                    to_read = min(remaining, chunk_size)
                    chunk = self._ctx.read(self._fh, to_read)
                    if not chunk:
                        break
                    chunks.append(chunk)
                    remaining -= len(chunk)
                    if len(chunk) < to_read:
                        # EOF reached
                        break
                data = b''.join(chunks)
            
            if self._binary:
                return data
            return data.decode(self._encoding, errors=self._errors)
        
        def readline(self, size: int = -1) -> Union[bytes, str]:
            """Read a line"""
            line = []
            while True:
                char = self.read(1)
                if not char:
                    break
                line.append(char)
                if char == (b'\n' if self._binary else '\n'):
                    break
                if size > 0 and len(line) >= size:
                    break
            
            if self._binary:
                return b''.join(line)
            return ''.join(line)
        
        def readlines(self) -> List[Union[bytes, str]]:
            """Read all lines"""
            lines = []
            while True:
                line = self.readline()
                if not line:
                    break
                lines.append(line)
            return lines
        
        def write(self, data: Union[bytes, str]) -> int:
            """Write data (automatically chunks large writes)"""
            if self._closed:
                raise ValueError("I/O operation on closed file")
            
            if isinstance(data, str):
                data = data.encode(self._encoding or 'utf-8', errors=self._errors or 'strict')
            
            # NFS has max request size limit (~1MB), chunk large writes
            # Chunk size configurable via IEOPS_NFS_WRITE_CHUNK_SIZE env var
            from ..config import env
            chunk_size = env.storage.write_chunk_size
            total_written = 0
            offset = 0
            
            while offset < len(data):
                chunk = data[offset:offset + chunk_size]
                written = self._ctx.write(self._fh, chunk)
                if written <= 0:
                    break
                total_written += written
                offset += written
            
            return total_written
        
        def writelines(self, lines):
            """Write multiple lines"""
            for line in lines:
                self.write(line)
        
        def seek(self, offset: int, whence: int = 0) -> int:
            """Move file pointer"""
            if self._closed:
                raise ValueError("I/O operation on closed file")
            return self._ctx.lseek(self._fh, offset, whence)
        
        def tell(self) -> int:
            """Return current position"""
            return self._ctx.lseek(self._fh, 0, Whence.SEEK_CUR)
        
        def truncate(self, size: Optional[int] = None) -> int:
            """Truncate file"""
            if size is None:
                size = self.tell()
            self._ctx.ftruncate(self._fh, size)
            return size
        
        def flush(self):
            """Flush buffer"""
            if not self._closed:
                self._ctx.fsync(self._fh)
        
        def fileno(self) -> int:
            """Return file descriptor (actually returns handle value)"""
            return self._fh.value if self._fh else -1
        
        def isatty(self) -> bool:
            return False
        
        def readable(self) -> bool:
            return 'r' in self._mode or '+' in self._mode
        
        def writable(self) -> bool:
            return 'w' in self._mode or 'a' in self._mode or '+' in self._mode
        
        def seekable(self) -> bool:
            return True
        
        # ============ Lock Operations ============
        
        def lock(self, exclusive: bool = True, blocking: bool = True, 
                 start: int = 0, length: int = 0) -> bool:
            """
            Acquire file lock
            
            Args:
                exclusive: True for exclusive lock, False for shared lock
                blocking: True for blocking mode, False for non-blocking
                start: Lock start position (0 means from file start)
                length: Lock length (0 means to end of file)
                
            Returns:
                True on success, may return False in non-blocking mode
            """
            if self._closed:
                raise ValueError("I/O operation on closed file")
            
            lock_type = LockType.F_WRLCK if exclusive else LockType.F_RDLCK
            cmd = FcntlOp.F_SETLKW if blocking else FcntlOp.F_SETLK
            
            result = self._ctx.fcntl_lock(self._fh, cmd, lock_type, start, length)
            if result:
                self._lock_held = True
            return result
        
        def unlock(self, start: int = 0, length: int = 0) -> bool:
            """
            Release file lock
            
            Args:
                start: Unlock start position
                length: Unlock length (0 means to end of file)
            """
            if self._closed:
                return False
            
            result = self._ctx.fcntl_lock(
                self._fh, FcntlOp.F_SETLK, LockType.F_UNLCK, start, length
            )
            if result:
                self._lock_held = False
            return result
        
        def try_lock(self, exclusive: bool = True, 
                     start: int = 0, length: int = 0) -> bool:
            """
            Try to acquire lock (non-blocking)
            
            Returns:
                True on success, False if lock is held by others
            """
            return self.lock(exclusive=exclusive, blocking=False, 
                            start=start, length=length)
        
        # ============ Context Manager ============
        
        def close(self):
            """Close file"""
            if not self._closed:
                if self._lock_held:
                    try:
                        self.unlock()
                    except:
                        pass
                self._ctx.close_file(self._fh)
                self._closed = True
        
        def __enter__(self):
            return self
        
        def __exit__(self, exc_type, exc_val, exc_tb):
            self.close()
            return False
        
        def __iter__(self):
            return self
        
        def __next__(self):
            line = self.readline()
            if not line:
                raise StopIteration
            return line
    
    
    # ============================================================================
    # NFS FileSystem Class (High-level Wrapper)
    # ============================================================================
    
    class NFSFileSystem:
        """
        NFS FileSystem Client
        
        Provides os/pathlib-like file system operation interface, supports:
        - File read/write
        - Directory operations
        - File locks (distributed locks)
        - Thread safety
        """
        
        def __init__(self, server: str, export: str = "/", port: Optional[int] = None):
            """
            Connect to NFS server
            
            Args:
                server: NFS server address (IP or hostname)
                export: Export path
                port: NFS port (optional, default 2049, requires libnfs 6.x+)
            """
            self._server = server
            self._export = export
            self._port = port
            self._local = threading.local()
        
        @property
        def _ctx(self) -> NFSContext:
            """Get current thread's NFS context (thread-safe)"""
            if not hasattr(self._local, 'ctx') or self._local.ctx is None:
                self._local.ctx = NFSContext(self._server, self._export, self._port)
            return self._local.ctx
        
        def close(self):
            """Close connection"""
            if hasattr(self._local, 'ctx') and self._local.ctx:
                self._local.ctx.close()
                self._local.ctx = None
        
        # ============ File Operations ============
        
        def open(self, path: str, mode: str = 'r', 
                 encoding: Optional[str] = None, errors: Optional[str] = None) -> NFSFile:
            """
            Open file
            
            Args:
                path: File path
                mode: Open mode ('r', 'w', 'a', 'r+', 'w+', 'a+', with 'b')
                encoding: Text encoding (default: 'utf-8', ignored in binary mode)
                errors: Encoding error handling ('strict', 'ignore', 'replace', etc.)
            """
            return NFSFile(self._ctx, path, mode, encoding, errors)
        
        # ============ Locked Operations ============
        
        @contextmanager
        def locked_open(self, path: str, mode: str = 'r', 
                        exclusive: bool = True, blocking: bool = True):
            """
            Open file with lock (context manager)
            
            Args:
                path: File path
                mode: Open mode
                exclusive: Whether exclusive lock
                blocking: Whether blocking wait
                
            Usage:
                with fs.locked_open('/data/file.txt', 'rw') as f:
                    content = f.read()
                    f.seek(0)
                    f.write(new_content)
            """
            f = self.open(path, mode)
            try:
                if not f.lock(exclusive=exclusive, blocking=blocking):
                    raise NFSLockError(f"Failed to acquire file lock: {path}")
                yield f
            finally:
                f.close()
        
        @contextmanager
        def lock_file(self, path: str, exclusive: bool = True, 
                      blocking: bool = True, timeout: float = None):
            """
            Lock file (without opening content)
            
            For scenarios requiring mutual exclusion without reading/writing file content
            
            Args:
                path: File path (must exist)
                exclusive: Whether exclusive lock
                blocking: Whether blocking
                timeout: Timeout in seconds (only effective in non-blocking mode)
            """
            f = self.open(path, 'r')
            try:
                if timeout and not blocking:
                    import time
                    start = time.time()
                    while not f.try_lock(exclusive=exclusive):
                        if time.time() - start > timeout:
                            raise NFSLockError(f"Lock timeout: {path}")
                        time.sleep(0.1)
                else:
                    if not f.lock(exclusive=exclusive, blocking=blocking):
                        raise NFSLockError(f"Failed to acquire lock: {path}")
                yield
            finally:
                f.close()
        
        # ============ Directory Operations ============
        
        def mkdir(self, path: str, parents: bool = False, exist_ok: bool = False):
            """
            Create directory
            
            Args:
                path: Directory path
                parents: Whether to create parent directories
                exist_ok: Whether to ignore if directory exists
            """
            if parents:
                parts = path.strip('/').split('/')
                current = ""
                for part in parts:
                    current = current + "/" + part
                    if not self.exists(current):
                        try:
                            self._ctx.mkdir(current)
                        except NFSError as e:
                            if not exist_ok or "exist" not in str(e).lower():
                                raise
            else:
                try:
                    self._ctx.mkdir(path)
                except NFSError as e:
                    if not exist_ok or "exist" not in str(e).lower():
                        raise
        
        def rmdir(self, path: str):
            """Remove empty directory"""
            self._ctx.rmdir(path)
        
        def listdir(self, path: str = "/") -> List[str]:
            """List directory contents (filenames only)"""
            entries = self._ctx.listdir(path)
            return [e.name for e in entries]
        
        def scandir(self, path: str = "/") -> List[DirEntry]:
            """List directory contents (with detailed info)"""
            return self._ctx.listdir(path)
        
        def walk(self, path: str = "/") -> Generator:
            """
            Recursively traverse directory
            
            Yields:
                (dirpath, dirnames, filenames) tuple
            """
            import stat as stat_module
            
            entries = self._ctx.listdir(path)
            dirs = []
            files = []
            
            for entry in entries:
                if stat_module.S_ISDIR(entry.mode):
                    dirs.append(entry.name)
                else:
                    files.append(entry.name)
            
            yield (path, dirs, files)
            
            for d in dirs:
                subpath = path.rstrip('/') + '/' + d
                yield from self.walk(subpath)
        
        # ============ File Management ============
        
        def exists(self, path: str) -> bool:
            """Check if path exists"""
            try:
                self._ctx.stat(path)
                return True
            except NFSError:
                return False
        
        def isfile(self, path: str) -> bool:
            """Check if path is a file"""
            try:
                st = self._ctx.stat(path)
                return st.is_file
            except NFSError:
                return False
        
        def isdir(self, path: str) -> bool:
            """Check if path is a directory"""
            try:
                st = self._ctx.stat(path)
                return st.is_dir
            except NFSError:
                return False
        
        def islink(self, path: str) -> bool:
            """Check if path is a symbolic link"""
            try:
                st = self._ctx.lstat(path)
                return st.is_link
            except NFSError:
                return False
        
        def stat(self, path: str) -> FileStat:
            """Get file status"""
            return self._ctx.stat(path)
        
        def lstat(self, path: str) -> FileStat:
            """Get file status (do not follow symlinks)"""
            return self._ctx.lstat(path)
        
        def getsize(self, path: str) -> int:
            """Get file size"""
            return self._ctx.stat(path).size
        
        def remove(self, path: str):
            """Remove file"""
            self._ctx.unlink(path)
        
        unlink = remove
        
        def rename(self, src: str, dst: str):
            """Rename/move file"""
            self._ctx.rename(src, dst)
        
        def copy(self, src: str, dst: str, buffer_size: int = 1024 * 1024):
            """Copy file"""
            with self.open(src, 'rb') as src_f:
                with self.open(dst, 'wb') as dst_f:
                    while True:
                        data = src_f.read(buffer_size)
                        if not data:
                            break
                        dst_f.write(data)
        
        def chmod(self, path: str, mode: int):
            """Change file permissions"""
            self._ctx.chmod(path, mode)
        
        def chown(self, path: str, uid: int, gid: int):
            """Change file owner"""
            self._ctx.chown(path, uid, gid)
        
        def truncate(self, path: str, length: int):
            """Truncate file"""
            self._ctx.truncate(path, length)
        
        def symlink(self, target: str, link_path: str):
            """Create symbolic link"""
            self._ctx.symlink(target, link_path)
        
        def link(self, src: str, dst: str):
            """Create hard link"""
            self._ctx.link(src, dst)
        
        # ============ Context Manager ============
        
        def __enter__(self):
            return self
        
        def __exit__(self, exc_type, exc_val, exc_tb):
            self.close()
            return False
    
    # ============================================================================
    # END OF DISABLED NFS CODE
    # ============================================================================
