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

namespace Firefly
{
    #region Lock Class
    // Simple lock class for synchronization
    internal class Lock
    {
        private readonly System.Threading.Lock _lock = new();

        public void Enter(Action action)
        {
            lock (_lock)
            {
                action();
            }
        }

        public T Enter<T>(Func<T> action)
        {
            lock (_lock)
            {
                return action();
            }
        }
    }
    #endregion

    partial class Firefly
    {
        #region Constants and Fields
        // Sharding configuration - each shard will have its own ConcurrentDictionary
        private const int ShardCount = 64; // Must be a power of 2
        private const int ShardMask = ShardCount - 1; // Used for fast modulo

        // In-memory storage for key-value pairs using ConcurrentDictionary for better performance
        private static readonly ConcurrentDictionary<string, string>[] stringStoreShards = CreateShards<ConcurrentDictionary<string, string>>();
        
        // In-memory storage for lists using ConcurrentDictionary for better performance
        private static readonly ConcurrentDictionary<string, List<string>>[] listStoreShards = CreateShards<ConcurrentDictionary<string, List<string>>>();
        // Use ReaderWriterLockSlim for list operations to allow concurrent reads
        private static readonly ReaderWriterLockSlim[] listStoreLocks = CreateListLocks();
        
        // In-memory storage for hash tables using ConcurrentDictionary for better performance
        private static readonly ConcurrentDictionary<string, ConcurrentDictionary<string, string>>[] hashStoreShards = CreateShards<ConcurrentDictionary<string, ConcurrentDictionary<string, string>>>();
        // We no longer need locks for most hash operations

        // Global lock for backup operations
        private static readonly Lock backupLock = new();

        // Default settings that can be overridden by command-line arguments
        private static int serverPort = 6379; // Default Redis port
        private static string bindAddress = "127.0.0.1"; // Default to localhost only
        private static bool backupsEnabled = true;
        private static int autoBackupIntervalMinutes = 5;
        private static int connectionTimeoutSeconds = 300; // 5 minutes default timeout
        private static string serverPassword = ""; // Default: no password required
        private static readonly Dictionary<string, bool> authenticatedClients = [];
        private static readonly ConcurrentDictionary<string, Queue<string>> clientCommandQueues = new();
        private static readonly int maxBatchSize = 1000; // Maximum number of commands to process in a batch
        private static readonly int maxPipelineSize = 10000; // Maximum number of commands in a pipeline
        private static readonly ConcurrentDictionary<string, bool> clientPipelineMode = new(); // Track if client is in pipeline mode

        // Network buffer settings
        private static readonly int bufferSize = 20480; // Increased from 1024 for better performance

        // Backup settings
        private static readonly string backupDirectory = "backups";
        private static readonly string backupFilePrefix = "firefly_data";
        private static readonly string backupFileExtension = ".fdb";
        private static int maxBackupFiles = 10;
        private static System.Timers.Timer? backupTimer;
        #endregion

        #region Main Entry Point
        static async Task Main(string[] args)
        {
            // Parse command line arguments
            await ParseArguments(args);

            // Initialize backup system if enabled
            if (backupsEnabled)
            {
                Console.WriteLine("Initializing backup system...");
                InitializeBackupSystem();
                
                // Load data from the latest backup
                LoadDataFromBackup();
            }

            // Start the server
            await StartServerAsync();
        }
        #endregion

        #region Command Processing
        static byte[] ProcessCommand(string message, string clientId)
        {
            // Trim whitespace and newlines
            message = message.Trim();

            // Split the command into parts, respecting quotes
            string[] parts = SplitRespectingQuotes(message);
            if (parts.Length == 0)
                return Encoding.UTF8.GetBytes("-ERR no command specified\r\n");

            string command = parts[0].ToUpperInvariant();

            try
            {
                // Always allow AUTH command
                if (command == "AUTH")
                {
                    if (parts.Length < 2)
                    {
                        return Encoding.UTF8.GetBytes("-ERR wrong number of arguments for 'auth' command\r\n");
                    }
                    return HandleAuthCommand(parts[1], clientId);
                }
                
                // Always allow PING for connectivity checks
                if (command == "PING")
                {
                    return Encoding.UTF8.GetBytes("+PONG\r\n");
                }
                
                // Always allow QUIT command for clean disconnection
                if (command == "QUIT")
                {
                    return Encoding.UTF8.GetBytes("+OK\r\n");
                }
                
                // Handle PIPELINE command
                if (command == "PIPELINE")
                {
                    clientPipelineMode[clientId] = true;
                    return Encoding.UTF8.GetBytes("+OK\r\n");
                }
                
                // Handle EXEC command
                if (command == "EXEC")
                {
                    clientPipelineMode[clientId] = false;
                    return Encoding.UTF8.GetBytes("+OK\r\n");
                }
                
                // Check authentication for all other commands if password is set
                if (!string.IsNullOrEmpty(serverPassword) && !IsAuthenticated(clientId))
                {
                    return Encoding.UTF8.GetBytes("-NOAUTH Authentication required.\r\n");
                }

                // Process different commands
                switch (command)
                {
                    case "ECHO":
                        if (parts.Length > 1)
                        {
                            string echoValue = parts[1];
                            return Encoding.UTF8.GetBytes($"+{echoValue}\r\n");
                        }
                        return Encoding.UTF8.GetBytes("-ERR wrong number of arguments for 'echo' command\r\n");

                    case "SET":
                        return HandleSetCommand(parts.Length > 1 ? string.Join(" ", parts.Skip(1)) : "");

                    case "GET":
                        return HandleGetCommand(parts.Length > 1 ? parts[1] : "");

                    case "DEL":
                        return HandleDelCommand(parts.Length > 1 ? parts[1] : "");

                    case "TYPE":
                        return HandleTypeCommand(parts.Length > 1 ? parts[1] : "");

                    case "LPUSH":
                        return HandleLPushCommand(parts.Length > 1 ? string.Join(" ", parts.Skip(1)) : "");

                    case "RPUSH":
                        return HandleRPushCommand(parts.Length > 1 ? string.Join(" ", parts.Skip(1)) : "");

                    case "LPOP":
                        return HandleLPopCommand(parts.Length > 1 ? parts[1] : "");

                    case "RPOP":
                        return HandleRPopCommand(parts.Length > 1 ? parts[1] : "");

                    case "LINDEX":
                        return HandleLIndexCommand(parts.Length > 1 ? string.Join(" ", parts.Skip(1)) : "");

                    case "LLEN":
                        return HandleLLengthCommand(parts.Length > 1 ? parts[1] : "");

                    case "LRANGE":
                        return HandleLRangeCommand(parts.Length > 1 ? string.Join(" ", parts.Skip(1)) : "");

                    case "LSET":
                        return HandleLSetCommand(parts.Length > 1 ? string.Join(" ", parts.Skip(1)) : "");
                    
                    case "LPOS":
                        return HandleLPosCommand(parts.Length > 1 ? string.Join(" ", parts.Skip(1)) : "");

                    case "LTRIM":
                        return HandleLTrimCommand(parts.Length > 1 ? string.Join(" ", parts.Skip(1)) : "");

                    case "LREM":
                        return HandleLRemCommand(parts.Length > 1 ? string.Join(" ", parts.Skip(1)) : "");

                    case "HSET":
                        return HandleHSetCommand(parts.Length > 1 ? string.Join(" ", parts.Skip(1)) : "");

                    case "HGET":
                        return HandleHGetCommand(parts.Length > 1 ? string.Join(" ", parts.Skip(1)) : "");

                    case "HDEL":
                        return HandleHDelCommand(parts.Length > 1 ? string.Join(" ", parts.Skip(1)) : "");

                    case "HEXISTS":
                        return HandleHExistsCommand(parts.Length > 1 ? string.Join(" ", parts.Skip(1)) : "");

                    case "HGETALL":
                        return HandleHGetAllCommand(parts.Length > 1 ? parts[1] : "");

                    case "HMSET":
                        return HandleHMSetCommand(parts.Length > 1 ? string.Join(" ", parts.Skip(1)) : "");

                    case "KEYS":
                        return HandleKeysCommand(parts.Length > 1 ? parts[1] : "*");

                    case "SAVE":
                        return HandleSaveCommand();

                    case "BGSAVE":
                        return HandleBgSaveCommand();

                    default:
                        return Encoding.UTF8.GetBytes($"-ERR unknown command '{command}'\r\n");
                }
            }
            catch (Exception ex)
            {
                // Catch any unexpected errors to prevent server crashes
                Console.WriteLine($"Error processing command: {ex.Message}");
                return Encoding.UTF8.GetBytes($"-ERR internal server error: {ex.Message}\r\n");
            }
        }
        #endregion
    }
}