using System.Runtime.InteropServices;
using System.Text;

namespace FireflyClient
{
    public partial class FireflyClient
    {
        // Native structure definitions for interop

        /// <summary>
        /// String array structure for interop
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        public struct NativeStringList
        {
            /// <summary>
            /// Pointer to an array of string pointers
            /// </summary>
            public IntPtr Strings;
            
            /// <summary>
            /// Number of strings in the array
            /// </summary>
            public int Count;
        }

        /// <summary>
        /// Key-value pair structure for interop
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        public struct NativeKeyValuePair
        {
            /// <summary>
            /// Pointer to the key string
            /// </summary>
            public IntPtr Key;
            
            /// <summary>
            /// Pointer to the value string
            /// </summary>
            public IntPtr Value;
        }

        /// <summary>
        /// Dictionary structure for interop
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        public struct NativeDictionary
        {
            /// <summary>
            /// Pointer to an array of key-value pairs
            /// </summary>
            public IntPtr Pairs;
            
            /// <summary>
            /// Number of pairs in the array
            /// </summary>
            public int Count;
        }

        // Helper to safely get client from handle
        private static FireflyClient? GetClientFromHandle(IntPtr handle)
        {
            if (handle == IntPtr.Zero) return null;
            try
            {
                var gch = (GCHandle)handle;
                return gch.Target as FireflyClient;
            }
            catch
            {
                return null; // Invalid handle
            }
        }

        // Helper to marshal string result to IntPtr
        private static IntPtr MarshalString(string? result)
        {
            if (result == null) return IntPtr.Zero;

            var bytes = Encoding.UTF8.GetBytes(result);
            var ptr = Marshal.AllocHGlobal(bytes.Length + 1);
            Marshal.Copy(bytes, 0, ptr, bytes.Length);
            Marshal.WriteByte(ptr + bytes.Length, 0); // Null terminator
            return ptr;
        }

        // Helper to marshal List<string> result to IntPtr (newline-delimited)
        private static IntPtr MarshalStringListResult(List<string>? result)
        {
             if (result == null || result.Count == 0) return IntPtr.Zero;
             var joinedResult = string.Join("\n", result); // Using \n as delimiter
             return MarshalString(joinedResult);
        }
        
        /// <summary>
        /// Marshals a List to a NativeStringList without string conversion
        /// </summary>
        private static NativeStringList MarshalStringList(List<string>? list)
        {
            if (list == null || list.Count == 0)
            {
                return new NativeStringList { Strings = IntPtr.Zero, Count = 0 };
            }

            // Allocate memory for the array of strings
            var arrayPtr = Marshal.AllocHGlobal(list.Count * IntPtr.Size);

            // Copy each string to the allocated memory
            for (var i = 0; i < list.Count; i++)
            {
                var stringPtr = MarshalString(list[i]);
                Marshal.WriteIntPtr(arrayPtr + i * IntPtr.Size, stringPtr);
            }

            return new NativeStringList { Strings = arrayPtr, Count = list.Count };
        }
        
        /// <summary>
        /// Marshals a Dictionary to a NativeDictionary without string conversion
        /// </summary>
        private static NativeDictionary MarshalDictionary(Dictionary<string, string>? dict)
        {
            if (dict == null || dict.Count == 0)
            {
                return new NativeDictionary { Pairs = IntPtr.Zero, Count = 0 };
            }

            // Allocate memory for the array of key-value pairs
            var structSize = Marshal.SizeOf<NativeKeyValuePair>();
            var pairsPtr = Marshal.AllocHGlobal(dict.Count * structSize);

            // For each key-value pair, create a NativeKeyValuePair
            var index = 0;
            foreach (var kvp in dict)
            {
                var keyPtr = MarshalString(kvp.Key);
                var valuePtr = MarshalString(kvp.Value);

                // Create a NativeKeyValuePair and write it to the allocated memory
                var pair = new NativeKeyValuePair
                {
                    Key = keyPtr,
                    Value = valuePtr
                };
                Marshal.StructureToPtr(pair, pairsPtr + index * structSize, false);
                index++;
            }

            return new NativeDictionary { Pairs = pairsPtr, Count = dict.Count };
        }

        #region Native Interop Methods

        /// <summary>
        /// Creates a new FireflyClient instance for native interop
        /// </summary>
        [UnmanagedCallersOnly(EntryPoint = "CreateClient")]
        public static IntPtr CreateClient(IntPtr hostPtr, int port)
        {
            try
            {
                // Use UTF8 for host string
                var host = Marshal.PtrToStringUTF8(hostPtr) ?? "127.0.0.1";
                var client = new FireflyClient(host, port);
                var handle = GCHandle.Alloc(client);
                return GCHandle.ToIntPtr(handle);
            }
            catch
            {
                return IntPtr.Zero;
            }
        }

        /// <summary>
        /// Destroys a FireflyClient instance created for native interop
        /// </summary>
        [UnmanagedCallersOnly(EntryPoint = "DestroyClient")]
        public static void DestroyClient(IntPtr handle)
        {
            if (handle != IntPtr.Zero)
            {
                try
                {
                    var gch = GCHandle.FromIntPtr(handle);
                    if (gch.Target is FireflyClient client)
                    {
                        client.Dispose();
                    }
                    // Check if handle is still allocated before freeing
                    if (gch.IsAllocated) 
                    {
                        gch.Free();
                    }
                }
                catch { /* Handle potential errors with invalid handles */ }
            }
        }

        /// <summary>
        /// Authenticates with the server using the provided password
        /// </summary>
        [UnmanagedCallersOnly(EntryPoint = "Authenticate")]
        public static bool NativeAuthenticate(IntPtr handle, IntPtr passwordPtr)
        {
            try
            {
                // Use UTF8 for password string
                var password = Marshal.PtrToStringUTF8(passwordPtr) ?? string.Empty;
                var client = GetClientFromHandle(handle);
                return client?.Authenticate(password) ?? false;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// Executes a raw command. Note: Argument parsing is basic.
        /// Consider using specific functions instead for reliability.
        /// </summary>
        [UnmanagedCallersOnly(EntryPoint = "ExecuteCommand")]
        public static IntPtr NativeExecuteCommand(IntPtr handle, IntPtr commandPtr, IntPtr argsPtr)
        {
            try
            {
                // Use UTF8 for command and args strings
                var command = Marshal.PtrToStringUTF8(commandPtr) ?? string.Empty;
                var args = Marshal.PtrToStringUTF8(argsPtr) ?? string.Empty;
                var client = GetClientFromHandle(handle);
                // WARNING: Basic split, unreliable if args have spaces
                string[] argArray = args.Split(' ', StringSplitOptions.RemoveEmptyEntries); 
                var result = client?.ExecuteCommand(command, argArray);
                return MarshalString(result);
            }
            catch
            {
                return IntPtr.Zero;
            }
        }

        /// <summary>
        /// Frees a string allocated by the native interop methods
        /// </summary>
        [UnmanagedCallersOnly(EntryPoint = "FreeString")]
        public static void NativeFreeString(IntPtr ptr)
        {
            if (ptr != IntPtr.Zero)
            {
                Marshal.FreeHGlobal(ptr);
            }
        }

        /// <summary>
        /// Frees a NativeStringList allocated by the native interop methods
        /// </summary>
        [UnmanagedCallersOnly(EntryPoint = "FreeStringList")]
        public static void NativeFreeStringList(NativeStringList list)
        {
            if (list.Strings != IntPtr.Zero && list.Count > 0)
            {
                for (var i = 0; i < list.Count; i++)
                {
                    var stringPtr = Marshal.ReadIntPtr(list.Strings + i * IntPtr.Size);
                    if (stringPtr != IntPtr.Zero)
                    {
                        Marshal.FreeHGlobal(stringPtr);
                    }
                }
                Marshal.FreeHGlobal(list.Strings);
            }
        }

        /// <summary>
        /// Frees a NativeDictionary allocated by the native interop methods
        /// </summary>
        [UnmanagedCallersOnly(EntryPoint = "FreeDictionary")]
        public static void NativeFreeDictionary(NativeDictionary dict)
        {
            if (dict.Pairs != IntPtr.Zero && dict.Count > 0)
            {
                var structSize = Marshal.SizeOf<NativeKeyValuePair>();

                for (var i = 0; i < dict.Count; i++)
                {
                    var pairPtr = dict.Pairs + i * structSize;
                    var pair = Marshal.PtrToStructure<NativeKeyValuePair>(pairPtr);
                    
                    if (pair.Key != IntPtr.Zero)
                        Marshal.FreeHGlobal(pair.Key);

                    if (pair.Value != IntPtr.Zero)
                        Marshal.FreeHGlobal(pair.Value);
                }
                Marshal.FreeHGlobal(dict.Pairs);
            }
        }

        // --- String Operations ---

        /// <summary>
        /// Sets a string value for a given key (Native Interop).
        /// </summary>
        /// <param name="handle">The GCHandle (as IntPtr) representing the client instance.</param>
        /// <param name="keyPtr">Pointer to a null-terminated UTF-8 string representing the key.</param>
        /// <param name="valuePtr">Pointer to a null-terminated UTF-8 string representing the value.</param>
        /// <returns>True if the command was successful (e.g., server replied OK), false otherwise.</returns>
        [UnmanagedCallersOnly(EntryPoint = "StringSet")]
        public static bool NativeStringSet(IntPtr handle, IntPtr keyPtr, IntPtr valuePtr)
        {
            try
            {
                var client = GetClientFromHandle(handle);
                var key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
                var value = Marshal.PtrToStringUTF8(valuePtr) ?? string.Empty;
                return client?.StringSet(key, value) ?? false;
            }
            catch { return false; }
        }

        /// <summary>
        /// Gets a string value for a given key (Native Interop).
        /// </summary>
        /// <param name="handle">The GCHandle (as IntPtr) representing the client instance.</param>
        /// <param name="keyPtr">Pointer to a null-terminated UTF-8 string representing the key.</param>
        /// <returns>Pointer to a null-terminated UTF-8 string result allocated via AllocHGlobal (caller must free with FreeString), or IntPtr.Zero on error or if key not found.</returns>
        [UnmanagedCallersOnly(EntryPoint = "StringGet")]
        public static IntPtr NativeStringGet(IntPtr handle, IntPtr keyPtr)
        {
            try
            {
                var client = GetClientFromHandle(handle);
                var key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
                var result = client?.StringGet(key);
                return MarshalString(result);
            }
            catch { return IntPtr.Zero; }
        }

        /// <summary>
        /// Deletes one or more keys (Native Interop).
        /// Note: Currently only supports deleting a single key via the C# Delete method.
        /// </summary>
        /// <param name="handle">The GCHandle (as IntPtr) representing the client instance.</param>
        /// <param name="keyPtr">Pointer to a null-terminated UTF-8 string representing the key to delete.</param>
        /// <returns>The number of keys that were removed (typically 1 or 0), or 0 on error.</returns>
        [UnmanagedCallersOnly(EntryPoint = "Delete")]
        public static int NativeDelete(IntPtr handle, IntPtr keyPtr)
        {
             try
             {
                 var client = GetClientFromHandle(handle);
                 var key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
                 // Assuming Delete returns the number of keys deleted (usually 1 or 0)
                 return client?.Delete(key) ?? 0; 
             }
             catch { return 0; }
        }

        // --- List Operations ---

        /// <summary>
        /// Adds a value to the beginning of a list (Native Interop).
        /// </summary>
        /// <param name="handle">The GCHandle (as IntPtr) representing the client instance.</param>
        /// <param name="keyPtr">Pointer to a null-terminated UTF-8 string representing the list key.</param>
        /// <param name="valuePtr">Pointer to a null-terminated UTF-8 string representing the value to add.</param>
        /// <returns>The length of the list after the push operation, or 0 on error.</returns>
        [UnmanagedCallersOnly(EntryPoint = "ListLeftPush")]
        public static int NativeListLeftPush(IntPtr handle, IntPtr keyPtr, IntPtr valuePtr)
        {
             try
             {
                 var client = GetClientFromHandle(handle);
                 var key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
                 var value = Marshal.PtrToStringUTF8(valuePtr) ?? string.Empty;
                 // Assuming LPUSH returns the new length of the list
                 return client?.ListLeftPush(key, value) ?? 0;
             }
             catch { return 0; }
        }

        /// <summary>
        /// Adds a value to the end of a list (Native Interop).
        /// </summary>
        /// <param name="handle">The GCHandle (as IntPtr) representing the client instance.</param>
        /// <param name="keyPtr">Pointer to a null-terminated UTF-8 string representing the list key.</param>
        /// <param name="valuePtr">Pointer to a null-terminated UTF-8 string representing the value to add.</param>
        /// <returns>The length of the list after the push operation, or 0 on error.</returns>
        [UnmanagedCallersOnly(EntryPoint = "ListRightPush")]
        public static int NativeListRightPush(IntPtr handle, IntPtr keyPtr, IntPtr valuePtr)
        {
             try
             {
                 var client = GetClientFromHandle(handle);
                 var key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
                 var value = Marshal.PtrToStringUTF8(valuePtr) ?? string.Empty;
                 // Assuming RPUSH returns the new length of the list
                 return client?.ListRightPush(key, value) ?? 0;
             }
             catch { return 0; }
        }

        /// <summary>
        /// Removes and returns the first element of a list (Native Interop).
        /// </summary>
        /// <param name="handle">The GCHandle (as IntPtr) representing the client instance.</param>
        /// <param name="keyPtr">Pointer to a null-terminated UTF-8 string representing the list key.</param>
        /// <returns>Pointer to a null-terminated UTF-8 string result allocated via AllocHGlobal (caller must free with FreeString), or IntPtr.Zero on error or if list is empty.</returns>
        [UnmanagedCallersOnly(EntryPoint = "ListLeftPop")]
        public static IntPtr NativeListLeftPop(IntPtr handle, IntPtr keyPtr)
        {
             try
             {
                 var client = GetClientFromHandle(handle);
                 var key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
                 var result = client?.ListLeftPop(key);
                 return MarshalString(result);
             }
             catch { return IntPtr.Zero; }
        }

        /// <summary>
        /// Removes and returns the last element of a list (Native Interop).
        /// </summary>
        /// <param name="handle">The GCHandle (as IntPtr) representing the client instance.</param>
        /// <param name="keyPtr">Pointer to a null-terminated UTF-8 string representing the list key.</param>
        /// <returns>Pointer to a null-terminated UTF-8 string result allocated via AllocHGlobal (caller must free with FreeString), or IntPtr.Zero on error or if list is empty.</returns>
        [UnmanagedCallersOnly(EntryPoint = "ListRightPop")]
        public static IntPtr NativeListRightPop(IntPtr handle, IntPtr keyPtr)
        {
             try
             {
                 var client = GetClientFromHandle(handle);
                 var key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
                 var result = client?.ListRightPop(key);
                 return MarshalString(result);
             }
             catch { return IntPtr.Zero; }
        }

        /// <summary>
        /// Gets the length of a list (Native Interop).
        /// </summary>
        /// <param name="handle">The GCHandle (as IntPtr) representing the client instance.</param>
        /// <param name="keyPtr">Pointer to a null-terminated UTF-8 string representing the list key.</param>
        /// <returns>The length of the list, or 0 on error.</returns>
        [UnmanagedCallersOnly(EntryPoint = "ListLength")]
        public static int NativeListLength(IntPtr handle, IntPtr keyPtr)
        {
            try
            {
                var client = GetClientFromHandle(handle);
                var key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
                var result = client?.ListLength(key) ?? 0;
                return result;
            }
            catch { return 0; }
        }

        /// <summary>
        /// Gets a range of elements from a list (Native Interop).
        /// </summary>
        /// <param name="handle">The GCHandle (as IntPtr) representing the client instance.</param>
        /// <param name="keyPtr">Pointer to a null-terminated UTF-8 string representing the list key.</param>
        /// <param name="start">The start index (0-based).</param>
        /// <param name="stop">The stop index (inclusive, use -1 for end).</param>
        /// <returns>Pointer to a null-terminated UTF-8 string containing newline-delimited list elements, allocated via AllocHGlobal (caller must free with FreeString), or IntPtr.Zero on error.</returns>
        /// <remarks>The returned IntPtr points to a single string. The native caller must parse this string (e.g., split by '\n') and free the pointer using NativeFreeString.</remarks>
        [UnmanagedCallersOnly(EntryPoint = "ListRange")]
        public static NativeStringList NativeListRange(IntPtr handle, IntPtr keyPtr, int start, int stop)
        {
            try
            {
                var client = GetClientFromHandle(handle);
                var key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
                List<string>? result = client?.ListRange(key, start, stop);
                return MarshalStringList(result);
            }
            catch 
            { 
                return new NativeStringList { Strings = IntPtr.Zero, Count = 0 };
            }
        }

        /// <summary>
        /// Gets an element from a list by its index (Native Interop).
        /// </summary>
        /// <param name="handle">The GCHandle (as IntPtr) representing the client instance.</param>
        /// <param name="keyPtr">Pointer to a null-terminated UTF-8 string representing the list key.</param>
        /// <param name="index">The index of the element (0-based).</param>
        /// <returns>Pointer to a null-terminated UTF-8 string result allocated via AllocHGlobal (caller must free with FreeString), or IntPtr.Zero on error or if index is out of range.</returns>
        [UnmanagedCallersOnly(EntryPoint = "ListIndex")]
        public static IntPtr NativeListIndex(IntPtr handle, IntPtr keyPtr, int index)
        {
             try
             {
                 var client = GetClientFromHandle(handle);
                 var key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
                 var result = client?.ListIndex(key, index);
                 return MarshalString(result);
             }
             catch { return IntPtr.Zero; }
        }

        /// <summary>
        /// Sets the value of an element in a list by its index (Native Interop).
        /// </summary>
        /// <param name="handle">The GCHandle (as IntPtr) representing the client instance.</param>
        /// <param name="keyPtr">Pointer to a null-terminated UTF-8 string representing the list key.</param>
        /// <param name="index">The index of the element to set (0-based).</param>
        /// <param name="valuePtr">Pointer to a null-terminated UTF-8 string representing the new value.</param>
        /// <returns>True if the command was successful, false otherwise (e.g., index out of range).</returns>
        [UnmanagedCallersOnly(EntryPoint = "ListSet")]
        public static bool NativeListSet(IntPtr handle, IntPtr keyPtr, int index, IntPtr valuePtr)
        {
             try
             {
                 var client = GetClientFromHandle(handle);
                 var key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
                 var value = Marshal.PtrToStringUTF8(valuePtr) ?? string.Empty;
                 return client?.ListSet(key, index, value) ?? false;
             }
             catch { return false; }
        }
        
        /// <summary>
        /// Returns the index of the first occurrence of an element in a list (Native Interop).
        /// Rank and MaxLen parameters are currently ignored.
        /// </summary>
        /// <param name="handle">The GCHandle (as IntPtr) representing the client instance.</param>
        /// <param name="keyPtr">Pointer to a null-terminated UTF-8 string representing the list key.</param>
        /// <param name="elementPtr">Pointer to a null-terminated UTF-8 string representing the element to find.</param>
        /// <param name="rank">Optional rank (ignored).</param>
        /// <param name="maxlen">Optional max length (ignored).</param>
        /// <returns>The 0-based index of the element, or -1 if not found or on error.</returns>
        [UnmanagedCallersOnly(EntryPoint = "ListPosition")]
        public static int NativeListPosition(IntPtr handle, IntPtr keyPtr, IntPtr elementPtr, int rank, int maxlen)
        {
             try
             {
                 var client = GetClientFromHandle(handle);
                 var key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
                 var element = Marshal.PtrToStringUTF8(elementPtr) ?? string.Empty;
                 // Note: C# ListPosition returns the first index or -1
                 var position = client?.ListPosition(key, element, rank, maxlen);
                 return position ?? -1;
             }
             catch { return -1; }
        }
        
        /// <summary>
        /// Trims a list to contain only the specified range of elements (Native Interop).
        /// </summary>
        /// <param name="handle">The GCHandle (as IntPtr) representing the client instance.</param>
        /// <param name="keyPtr">Pointer to a null-terminated UTF-8 string representing the list key.</param>
        /// <param name="start">The start index (0-based).</param>
        /// <param name="stop">The stop index (inclusive, use -1 for end).</param>
        /// <returns>True if the command was successful, false otherwise.</returns>
        [UnmanagedCallersOnly(EntryPoint = "ListTrim")]
        public static bool NativeListTrim(IntPtr handle, IntPtr keyPtr, int start, int stop)
        {
             try
             {
                 var client = GetClientFromHandle(handle);
                 var key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
                 return client?.ListTrim(key, start, stop) ?? false;
             }
             catch { return false; }
        }
        
        /// <summary>
        /// Removes occurrences of elements from a list (Native Interop).
        /// </summary>
        /// <param name="handle">The GCHandle (as IntPtr) representing the client instance.</param>
        /// <param name="keyPtr">Pointer to a null-terminated UTF-8 string representing the list key.</param>
        /// <param name="count">Number of occurrences to remove (see C# ListRemove docs).</param>
        /// <param name="elementPtr">Pointer to a null-terminated UTF-8 string representing the element to remove.</param>
        /// <returns>The number of elements removed, or 0 on error.</returns>
        [UnmanagedCallersOnly(EntryPoint = "ListRemove")]
        public static int NativeListRemove(IntPtr handle, IntPtr keyPtr, int count, IntPtr elementPtr)
        {
             try
             {
                 var client = GetClientFromHandle(handle);
                 var key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
                 var element = Marshal.PtrToStringUTF8(elementPtr) ?? string.Empty;
                 return client?.ListRemove(key, count, element) ?? 0;
             }
             catch { return 0; }
        }
        
        // --- Hash Operations ---

        /// <summary>
        /// Sets the value of a field within a hash (Native Interop).
        /// </summary>
        /// <param name="handle">The GCHandle (as IntPtr) representing the client instance.</param>
        /// <param name="keyPtr">Pointer to a null-terminated UTF-8 string representing the hash key.</param>
        /// <param name="fieldPtr">Pointer to a null-terminated UTF-8 string representing the field name.</param>
        /// <param name="valuePtr">Pointer to a null-terminated UTF-8 string representing the value to set.</param>
        /// <returns>True if the field was new or updated successfully, false on error.</returns>
        [UnmanagedCallersOnly(EntryPoint = "HashSet")]
        public static bool NativeHashSet(IntPtr handle, IntPtr keyPtr, IntPtr fieldPtr, IntPtr valuePtr)
        {
            try
            {
                var client = GetClientFromHandle(handle);
                var key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
                var field = Marshal.PtrToStringUTF8(fieldPtr) ?? string.Empty;
                var value = Marshal.PtrToStringUTF8(valuePtr) ?? string.Empty;
                // Assuming HSET returns 1 if field is new, 0 if updated
                return client?.HashSet(key, field, value) ?? false; // Adapt if C# returns differently
            }
            catch { return false; }
        }

        /// <summary>
        /// Gets the value of a field within a hash (Native Interop).
        /// </summary>
        /// <param name="handle">The GCHandle (as IntPtr) representing the client instance.</param>
        /// <param name="keyPtr">Pointer to a null-terminated UTF-8 string representing the hash key.</param>
        /// <param name="fieldPtr">Pointer to a null-terminated UTF-8 string representing the field name.</param>
        /// <returns>Pointer to a null-terminated UTF-8 string result allocated via AllocHGlobal (caller must free with FreeString), or IntPtr.Zero on error or if field/key not found.</returns>
        [UnmanagedCallersOnly(EntryPoint = "HashGet")]
        public static IntPtr NativeHashGet(IntPtr handle, IntPtr keyPtr, IntPtr fieldPtr)
        {
            try
            {
                var client = GetClientFromHandle(handle);
                var key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
                var field = Marshal.PtrToStringUTF8(fieldPtr) ?? string.Empty;
                var result = client?.HashGet(key, field);
                return MarshalString(result);
            }
            catch { return IntPtr.Zero; }
        }

        /// <summary>
        /// Deletes a field from a hash (Native Interop).
        /// </summary>
        /// <param name="handle">The GCHandle (as IntPtr) representing the client instance.</param>
        /// <param name="keyPtr">Pointer to a null-terminated UTF-8 string representing the hash key.</param>
        /// <param name="fieldPtr">Pointer to a null-terminated UTF-8 string representing the field to delete.</param>
        /// <returns>True if the field was deleted, false otherwise (e.g., field/key not found).</returns>
        [UnmanagedCallersOnly(EntryPoint = "HashDelete")]
        public static bool NativeHashDelete(IntPtr handle, IntPtr keyPtr, IntPtr fieldPtr)
        {
             try
             {
                 var client = GetClientFromHandle(handle);
                 var key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
                 var field = Marshal.PtrToStringUTF8(fieldPtr) ?? string.Empty;
                 // Assuming HDEL returns true if field was deleted, false otherwise
                 return client?.HashDelete(key, field) ?? false;
             }
             catch { return false; }
        }

        /// <summary>
        /// Checks if a field exists within a hash (Native Interop).
        /// </summary>
        /// <param name="handle">The GCHandle (as IntPtr) representing the client instance.</param>
        /// <param name="keyPtr">Pointer to a null-terminated UTF-8 string representing the hash key.</param>
        /// <param name="fieldPtr">Pointer to a null-terminated UTF-8 string representing the field name.</param>
        /// <returns>True if the field exists, false otherwise.</returns>
        [UnmanagedCallersOnly(EntryPoint = "HashFieldExists")]
        public static bool NativeHashFieldExists(IntPtr handle, IntPtr keyPtr, IntPtr fieldPtr)
        {
             try
             {
                 var client = GetClientFromHandle(handle);
                 var key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
                 var field = Marshal.PtrToStringUTF8(fieldPtr) ?? string.Empty;
                 return client?.HashFieldExists(key, field) ?? false;
             }
             catch { return false; }
        }

        /// <summary>
        /// Sets multiple fields and values in a hash (Native Interop).
        /// Parses a space-separated string of field-value pairs.
        /// </summary>
        /// <param name="handle">The GCHandle (as IntPtr) representing the client instance.</param>
        /// <param name="keyPtr">Pointer to a null-terminated UTF-8 string representing the hash key.</param>
        /// <param name="fieldValuePairsPtr">Pointer to a null-terminated UTF-8 string of space-separated field-value pairs.</param>
        /// <returns>True if successful, false on error (e.g., odd number of pairs).</returns>
        [UnmanagedCallersOnly(EntryPoint = "HashMultiSet")]
        public static bool NativeHashMultiSet(IntPtr handle, IntPtr keyPtr, IntPtr fieldValuePairsPtr)
        {
            try
            {
                var client = GetClientFromHandle(handle);
                var key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
                var fieldValuePairs = Marshal.PtrToStringUTF8(fieldValuePairsPtr) ?? string.Empty;
                
                string[] pairs = fieldValuePairs.Split(' ', StringSplitOptions.RemoveEmptyEntries);
                if (pairs.Length % 2 != 0)
                {
                    // Invalid input: odd number of elements
                    return false; 
                }
                
                var dict = new Dictionary<string, string>();
                for (var i = 0; i < pairs.Length; i += 2)
                {
                    dict[pairs[i]] = pairs[i + 1];
                }
                
                return client?.HashMultiSet(key, dict) ?? false;
            }
            catch { return false; }
        }

        /// <summary>
        /// Gets all fields and values from a hash (Native Interop).
        /// </summary>
        /// <param name="handle">The GCHandle (as IntPtr) representing the client instance.</param>
        /// <param name="keyPtr">Pointer to a null-terminated UTF-8 string representing the hash key.</param>
        /// <returns>Pointer to a null-terminated UTF-8 string containing newline-delimited "field=value" pairs, allocated via AllocHGlobal (caller must free with FreeString), or IntPtr.Zero on error or if hash not found.</returns>
        /// <remarks>The returned IntPtr points to a single string. The native caller must parse this string (e.g., split by '\n', then by '=') and free the pointer using NativeFreeString.</remarks>
        [UnmanagedCallersOnly(EntryPoint = "HashGetAll")]
        public static NativeDictionary NativeHashGetAll(IntPtr handle, IntPtr keyPtr)
        {
            try
            {
                var client = GetClientFromHandle(handle);
                var key = Marshal.PtrToStringUTF8(keyPtr) ?? string.Empty;
                Dictionary<string, string>? result = client?.HashGetAll(key);
                return MarshalDictionary(result);
            }
            catch
            {
                return new NativeDictionary { Pairs = IntPtr.Zero, Count = 0 };
            }
        }

        // --- Pipeline Operations (Already Implemented) ---

        /// <summary>
        /// Enables or disables pipeline mode for the client (Native Interop).
        /// </summary>
        /// <param name="handle">The GCHandle (as IntPtr) representing the client instance.</param>
        /// <param name="enabled">True to enable pipeline mode, false to disable.</param>
        /// <returns>True if the mode was set successfully, false on error.</returns>
        [UnmanagedCallersOnly(EntryPoint = "SetPipelineMode")]
        public static bool NativeSetPipelineMode(IntPtr handle, bool enabled)
        {
            try
            {
                var client = GetClientFromHandle(handle);
                if (client == null) return false;
                
                client.SetPipelineMode(enabled);
                return true;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// Sets the maximum number of commands to batch in pipeline mode (Native Interop).
        /// </summary>
        /// <param name="handle">The GCHandle (as IntPtr) representing the client instance.</param>
        /// <param name="size">The maximum number of commands to queue before sending.</param>
        /// <returns>True if the batch size was set successfully, false on error (e.g., invalid size).</returns>
        [UnmanagedCallersOnly(EntryPoint = "SetBatchSize")]
        public static bool NativeSetBatchSize(IntPtr handle, int size)
        {
            try
            {
                var client = GetClientFromHandle(handle);
                if (client == null) return false;
                
                client.SetBatchSize(size);
                return true;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// Sends all queued commands to the server immediately (Native Interop).
        /// </summary>
        /// <param name="handle">The GCHandle (as IntPtr) representing the client instance.</param>
        /// <returns>Pointer to a null-terminated UTF-8 string containing the server's combined response, allocated via AllocHGlobal (caller must free with FreeString), or IntPtr.Zero on error or if queue was empty.</returns>
        [UnmanagedCallersOnly(EntryPoint = "FlushPipeline")]
        public static IntPtr NativeFlushPipeline(IntPtr handle)
        {
            try
            {
                var client = GetClientFromHandle(handle);
                var response = client?.FlushPipeline();
                return MarshalString(response);
            }
            catch
            {
                return IntPtr.Zero;
            }
        }

        /// <summary>
        /// Gets the number of commands currently waiting in the pipeline queue (Native Interop).
        /// </summary>
        /// <param name="handle">The GCHandle (as IntPtr) representing the client instance.</param>
        /// <returns>The number of queued commands, or 0 on error.</returns>
        [UnmanagedCallersOnly(EntryPoint = "GetQueuedCommandCount")]
        public static int NativeGetQueuedCommandCount(IntPtr handle)
        {
            try
            {
                var client = GetClientFromHandle(handle);
                return client?.QueuedCommandCount ?? 0;
            }
            catch
            {
                return 0;
            }
        }

        /// <summary>
        /// Checks if pipeline mode is currently enabled for the client (Native Interop).
        /// </summary>
        /// <param name="handle">The GCHandle (as IntPtr) representing the client instance.</param>
        /// <returns>True if pipeline mode is enabled, false otherwise or on error.</returns>
        [UnmanagedCallersOnly(EntryPoint = "IsPipelineMode")]
        public static bool NativeIsPipelineMode(IntPtr handle)
        {
            try
            {
                var client = GetClientFromHandle(handle);
                return client?.IsPipelineMode ?? false;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// Gets the current maximum batch size configured for pipeline mode (Native Interop).
        /// </summary>
        /// <param name="handle">The GCHandle (as IntPtr) representing the client instance.</param>
        /// <returns>The maximum batch size, or 0 on error.</returns>
        [UnmanagedCallersOnly(EntryPoint = "GetBatchSize")]
        public static int NativeGetBatchSize(IntPtr handle)
        {
            try
            {
                var client = GetClientFromHandle(handle);
                return client?.MaxBatchSize ?? 0;
            }
            catch
            {
                return 0;
            }
        }

        /// <summary>
        /// Gets all keys matching the specified pattern (Native Interop).
        /// </summary>
        /// <param name="handle">The GCHandle (as IntPtr) representing the client instance.</param>
        /// <param name="patternPtr">Pointer to a null-terminated UTF-8 string representing the pattern to match against keys.</param>
        /// <returns>Pointer to a null-terminated UTF-8 string containing newline-delimited keys, allocated via AllocHGlobal (caller must free with FreeString), or IntPtr.Zero on error.</returns>
        [UnmanagedCallersOnly(EntryPoint = "Keys")]
        public static IntPtr NativeKeys(IntPtr handle, IntPtr patternPtr)
        {
            try
            {
                var client = GetClientFromHandle(handle);
                var pattern = Marshal.PtrToStringUTF8(patternPtr) ?? "*";
                List<string>? result = client?.Keys(pattern);
                return MarshalStringListResult(result);
            }
            catch
            {
                return IntPtr.Zero;
            }
        }

        #endregion
    }
} 