using System.Text;
using System.Collections.Concurrent;

namespace Firefly
{
    public partial class Firefly
    {
        #region Hash Operations
        /// <summary>
        /// Handles the HSET command which sets a field in a hash.
        /// </summary>
        /// <param name="args">Command arguments in format: "key field value"</param>
        /// <returns>1 if the field was added, 0 if it was updated</returns>
        static byte[] HandleHSetCommand(string args)
        {
            string[] parts = SplitRespectingQuotes(args);
            if (parts.Length < 3)
            {
                return Encoding.UTF8.GetBytes("-ERR wrong number of arguments for 'hset' command\r\n");
            }

            string key = parts[0];
            string field = parts[1];
            string value = parts.Length == 3 ? parts[2] : string.Join(" ", parts.Skip(2));

            try
            {
                var hash = GetOrCreateHash(key);
                bool isNewField = hash.TryAdd(field, value);

                if (!isNewField)
                    hash[field] = value;

                MarkDataAsModified();

                return Encoding.UTF8.GetBytes($":{(isNewField ? 1 : 0)}\r\n");
            }
            catch (InvalidOperationException ex)
            {
                // Handle the case where the key already exists with a different type
                string? existingType = GetKeyType(key);
                if (existingType != null)
                {
                    return Encoding.UTF8.GetBytes($"-ERR key '{key}' already exists as type '{existingType}'\r\n");
                }
                return Encoding.UTF8.GetBytes($"-ERR {ex.Message}\r\n");
            }
        }

        /// <summary>
        /// Handles the HGET command which retrieves the value of a field in a hash.
        /// </summary>
        /// <param name="args">Command arguments in format: "key field"</param>
        /// <returns>The value of the field, or nil if the field doesn't exist</returns>
        static byte[] HandleHGetCommand(string args)
        {
            string[] parts = SplitRespectingQuotes(args);
            if (parts.Length < 2)
            {
                return Encoding.UTF8.GetBytes("-ERR wrong number of arguments for 'hget' command\r\n");
            }

            string key = parts[0];
            string field = parts[1];

            if (HashStoreGet(key, out ConcurrentDictionary<string, string>? hash) && 
                hash != null && hash.TryGetValue(field, out string? fieldValue))
            {
                return Encoding.UTF8.GetBytes($"+{fieldValue}\r\n");
            }
            
            return Encoding.UTF8.GetBytes("$-1\r\n");
        }

        /// <summary>
        /// Handles the HDEL command which removes a field from a hash.
        /// </summary>
        /// <param name="args">Command arguments in format: "key field"</param>
        /// <returns>1 if the field was removed, 0 if it didn't exist</returns>
        static byte[] HandleHDelCommand(string args)
        {
            string[] parts = SplitRespectingQuotes(args);
            if (parts.Length < 2)
            {
                return Encoding.UTF8.GetBytes("-ERR wrong number of arguments for 'hdel' command\r\n");
            }

            string key = parts[0];
            string field = parts[1];

            if (HashStoreGet(key, out ConcurrentDictionary<string, string>? hash) && hash != null)
            {
                bool removed = hash.TryRemove(field, out _);

                if (removed)
                    MarkDataAsModified();

                if (hash.IsEmpty)
                    HashStoreRemove(key);

                return Encoding.UTF8.GetBytes($":{(removed ? 1 : 0)}\r\n");
            }
            
            return Encoding.UTF8.GetBytes(":0\r\n");
        }

        /// <summary>
        /// Handles the HEXISTS command which checks if a field exists in a hash.
        /// </summary>
        /// <param name="args">Command arguments in format: "key field"</param>
        /// <returns>1 if the field exists, 0 if it doesn't</returns>
        static byte[] HandleHExistsCommand(string args)
        {
            string[] parts = SplitRespectingQuotes(args);
            if (parts.Length < 2)
            {
                return Encoding.UTF8.GetBytes("-ERR wrong number of arguments for 'hexists' command\r\n");
            }

            string key = parts[0];
            string field = parts[1];

            if (HashStoreGet(key, out ConcurrentDictionary<string, string>? hash) && 
                hash != null && hash.TryGetValue(field, out _))
            {
                return Encoding.UTF8.GetBytes(":1\r\n");
            }

            return Encoding.UTF8.GetBytes(":0\r\n");
        }

        /// <summary>
        /// Handles the HGETALL command which retrieves all fields and values in a hash.
        /// </summary>
        /// <param name="args">Command arguments in format: "key"</param>
        /// <returns>All fields and values in the hash, or nil if the hash doesn't exist</returns>
        static byte[] HandleHGetAllCommand(string args)
        {
            if (string.IsNullOrWhiteSpace(args))
            {
                return Encoding.UTF8.GetBytes("-ERR wrong number of arguments for 'hgetall' command\r\n");
            }

            string key = args.Trim();
            
            if (HashStoreGet(key, out ConcurrentDictionary<string, string>? hash) && 
                hash != null && !hash.IsEmpty)
            {
                var hashSnapshot = hash.ToArray();
                StringBuilder response = new();
                response.Append("*\r\n");

                foreach (var kvp in hashSnapshot)
                {
                    response.Append($"+{kvp.Key}\r\n");
                    response.Append($"+{kvp.Value}\r\n");
                }

                return Encoding.UTF8.GetBytes(response.ToString());
            }
            
            return Encoding.UTF8.GetBytes("*\r\n");
        }

        /// <summary>
        /// Handles the HMSET command which sets multiple fields in a hash.
        /// </summary>
        /// <param name="args">Command arguments in format: "key field value [field value ...]"</param>
        /// <returns>OK on success, error if arguments are invalid</returns>
        static byte[] HandleHMSetCommand(string args)
        {
            string[] parts = SplitRespectingQuotes(args);
            if (parts.Length < 3 || (parts.Length - 1) % 2 != 0)
            {
                return Encoding.UTF8.GetBytes("-ERR wrong number of arguments for 'hmset' command\r\n");
            }

            string key = parts[0];
            var hash = GetOrCreateHash(key);
            
            try
            {
                for (int i = 1; i < parts.Length; i += 2)
                {
                    string field = parts[i];
                    if (i + 1 < parts.Length)
                    {
                        string value = parts[i + 1];
                        if (value.StartsWith('"') && value.EndsWith('"') && value.Length >= 2)
                        {
                            value = value[1..^1];
                        }
                        hash[field] = value;
                    }
                }
            }
            catch (Exception ex)
            {
                return Encoding.UTF8.GetBytes($"-ERR internal error: {ex.Message}\r\n");
            }

            MarkDataAsModified();
            
            return Encoding.UTF8.GetBytes("+OK\r\n");
        }

        #region Hash Store Helpers
        /// <summary>
        /// Gets or creates a hash for a given key.
        /// </summary>
        /// <param name="key">The key to get or create the hash for</param>
        /// <returns>The hash</returns>
        private static ConcurrentDictionary<string, string> GetOrCreateHash(string key)
        {
            int shardIndex = GetShardIndex(key);
            
            // If the key doesn't exist in this shard, check if it exists in any other store
            if (!hashStoreShards[shardIndex].ContainsKey(key))
            {
                // Check if key exists in any other store
                if (!EnsureKeyDoesNotExist(key, "hash"))
                {
                    throw new InvalidOperationException($"Key '{key}' already exists with a different type");
                }
            }
            
            var hash = hashStoreShards[shardIndex].GetOrAdd(key, _ => new ConcurrentDictionary<string, string>());

            if (hash.IsEmpty)
                MarkDataAsModified();
            
            return hash;
        }

        /// <summary>
        /// Checks if a hash exists for a given key.
        /// </summary>
        /// <param name="key">The key to check the hash for</param>
        /// <returns>True if the hash exists, false otherwise</returns>
        private static bool HashStoreExists(string key)
        {
            int shardIndex = GetShardIndex(key);
            return hashStoreShards[shardIndex].ContainsKey(key);
        }

        /// <summary>
        /// Gets a hash for a given key.
        /// </summary>
        /// <param name="key">The key to get the hash for</param>
        /// <param name="value">The hash</param>
        /// <returns>True if the hash was found, false otherwise</returns>
        private static bool HashStoreGet(string key, out ConcurrentDictionary<string, string>? value)
        {
            int shardIndex = GetShardIndex(key);
            return hashStoreShards[shardIndex].TryGetValue(key, out value);
        }

        /// <summary>
        /// Removes a hash for a given key.
        /// </summary>
        /// <param name="key">The key to remove the hash for</param>
        /// <returns>True if the hash was removed, false otherwise</returns>
        private static bool HashStoreRemove(string key)
        {
            int shardIndex = GetShardIndex(key);
            bool result = hashStoreShards[shardIndex].TryRemove(key, out _);
            
            if (result)
                MarkDataAsModified();
            
            return result;
        }
        #endregion
        #endregion
    }
}