using System.Collections.Concurrent;

namespace Firefly
{
    /// <summary>
    /// Core class implementing the Firefly database server functionality
    /// </summary>
    public partial class Firefly
    {
        #region Backup System
        // Track if data has been modified since last backup
        private static volatile bool dataModified = false;
        // Debounce period in milliseconds for data modification backups
        private static readonly int dataModificationBackupDelayMs = 5000;
        // Timer for debouncing data modification backups
        private static System.Timers.Timer? modificationBackupTimer;
        
        static void InitializeBackupSystem()
        {
            // Create backup directory if it doesn't exist
            if (!Directory.Exists(backupDirectory))
            {
                try
                {
                    Directory.CreateDirectory(backupDirectory);
                    Console.WriteLine($"Created backup directory: {backupDirectory}");
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"Warning: Failed to create backup directory: {ex.Message}");
                    Console.WriteLine("Backups will be disabled");
                    backupsEnabled = false;
                    return;
                }
            }

            // Set up automatic backup timer
            backupTimer = new System.Timers.Timer(autoBackupIntervalMinutes * 60 * 1000); // Convert minutes to milliseconds
            backupTimer.Elapsed += (sender, e) => BackupData();
            backupTimer.AutoReset = true;
            backupTimer.Start();

            // Set up data modification backup timer
            modificationBackupTimer = new System.Timers.Timer(dataModificationBackupDelayMs);
            modificationBackupTimer.Elapsed += (sender, e) => 
            {
                if (dataModified)
                {
                    BackupData();
                    dataModified = false;
                }
                modificationBackupTimer.Stop(); // Stop timer after backup
            };
            modificationBackupTimer.AutoReset = false;

            Console.WriteLine($"Automatic backup system initialized (every {autoBackupIntervalMinutes} minutes)");
            Console.WriteLine($"Data modification backup system initialized (after {dataModificationBackupDelayMs/1000} seconds of inactivity)");
            Console.WriteLine($"Keeping up to {maxBackupFiles} backup files");

            // Register backup on exit
            AppDomain.CurrentDomain.ProcessExit += (sender, e) =>
            {
                if (backupsEnabled)
                {
                    Console.WriteLine("Server shutting down, performing final backup...");
                    BackupData();
                }
            };
        }

        // Call this method whenever data is modified
        internal static void MarkDataAsModified()
        {
            if (!backupsEnabled)
                return;
                
            dataModified = true;
                
            // Reset and start the timer to debounce multiple rapid changes
            if (modificationBackupTimer != null)
            {
                modificationBackupTimer.Stop();
                modificationBackupTimer.Start();
            }
        }

        static void LoadDataFromBackup()
        {
            if (!backupsEnabled)
            {
                Console.WriteLine("Backups are disabled, starting with empty database");
                return;
            }

            string? mostRecentBackup = GetMostRecentBackupFile();
            if (mostRecentBackup != null)
            {
                Console.WriteLine($"Loading data from backup: {Path.GetFileName(mostRecentBackup)}");
                try
                {
                    using var fileStream = new FileStream(mostRecentBackup, FileMode.Open, FileAccess.Read);
                    using var binaryReader = new BinaryReader(fileStream);

                    // Read backup time
                    long backupTimeBinary = binaryReader.ReadInt64();
                    DateTime backupTime = DateTime.FromBinary(backupTimeBinary);
                    Console.WriteLine($"Backup was created on: {backupTime:f}");

                    // Read string store
                    int stringCount = binaryReader.ReadInt32();
                    Console.WriteLine($"Loading {stringCount} string keys from backup");
                    for (int i = 0; i < stringCount; i++)
                    {
                        string key = binaryReader.ReadString();
                        string value = binaryReader.ReadString();
                        int shardIndex = GetShardIndex(key);
                        stringStoreShards[shardIndex][key] = value;
                    }
                    Console.WriteLine($"Loaded {stringCount} string keys from backup");

                    // Read list store
                    int listCount = binaryReader.ReadInt32();
                    Console.WriteLine($"Loading {listCount} list keys from backup");
                    for (int i = 0; i < listCount; i++)
                    {
                        string key = binaryReader.ReadString();
                        int itemCount = binaryReader.ReadInt32();
                        var list = new List<string>();

                        for (int j = 0; j < itemCount; j++)
                        {
                            string item = binaryReader.ReadString();
                            list.Add(item);
                        }

                        int shardIndex = GetShardIndex(key);
                        listStoreLocks[shardIndex].EnterWriteLock();
                        try
                        {
                            listStoreShards[shardIndex][key] = list;
                        }
                        finally
                        {
                            listStoreLocks[shardIndex].ExitWriteLock();
                        }
                    }
                    Console.WriteLine($"Loaded {listCount} list keys from backup");

                    // Read hash store
                    int hashCount = binaryReader.ReadInt32();
                    Console.WriteLine($"Loading {hashCount} hash keys from backup");
                    for (int i = 0; i < hashCount; i++)
                    {
                        string key = binaryReader.ReadString();
                        int fieldCount = binaryReader.ReadInt32();
                        var concurrentHash = new ConcurrentDictionary<string, string>();

                        for (int j = 0; j < fieldCount; j++)
                        {
                            string fieldKey = binaryReader.ReadString();
                            string fieldValue = binaryReader.ReadString();
                            concurrentHash[fieldKey] = fieldValue;
                        }

                        int shardIndex = GetShardIndex(key);
                        hashStoreShards[shardIndex][key] = concurrentHash;
                    }
                    Console.WriteLine($"Loaded {hashCount} hash keys from backup");

                    Console.WriteLine($"Data successfully loaded from backup: {Path.GetFileName(mostRecentBackup)}");
                    Console.WriteLine($"Total keys loaded: {stringCount + listCount + hashCount}");
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"Error loading data from backup: {ex.Message}");
                    Console.WriteLine($"Stack trace: {ex.StackTrace}");
                    Console.WriteLine("The backup file may be corrupted or created with an incompatible version");
                    Console.WriteLine("Starting with empty database");
                }
            }
            else
            {
                Console.WriteLine("No backup files found, starting with empty database");
            }
        }

        // Get the most recent backup file
        static string? GetMostRecentBackupFile()
        {
            if (!Directory.Exists(backupDirectory))
                return null;

            try
            {
                var backupFiles = Directory.GetFiles(backupDirectory, backupFilePrefix + "*" + backupFileExtension)
                    .OrderByDescending(file => File.GetLastWriteTime(file))
                    .ToArray();

                return backupFiles.Length > 0 ? backupFiles[0] : null;
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error accessing backup files: {ex.Message}");
                return null;
            }
        }

        // Deletes oldest backup files if the number exceeds maxBackupFiles
        static void RotateBackupFiles()
        {
            if (!Directory.Exists(backupDirectory))
                return;

            try
            {
                var backupFiles = Directory.GetFiles(backupDirectory, backupFilePrefix + "*" + backupFileExtension)
                    .OrderByDescending(file => File.GetLastWriteTime(file))
                    .ToArray();

                // Remove oldest files if we have more than maxBackupFiles
                if (backupFiles.Length > maxBackupFiles)
                {
                    for (int i = maxBackupFiles; i < backupFiles.Length; i++)
                    {
                        try
                        {
                            File.Delete(backupFiles[i]);
                            Console.WriteLine($"Deleted old backup file: {Path.GetFileName(backupFiles[i])}");
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine($"Error deleting old backup file {backupFiles[i]}: {ex.Message}");
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error during backup rotation: {ex.Message}");
            }
        }

        static void BackupData()
        {
            // Skip if backups are disabled
            if (!backupsEnabled)
                return;

            // Use a global lock to prevent backup conflicts
            backupLock.Enter(() =>
            {
                try
                {
                    var data = new FireflyData
                    {
                        BackupTime = DateTime.Now
                    };

                    // Copy string store from all shards
                    for (int i = 0; i < ShardCount; i++)
                    {
                        foreach (var kvp in stringStoreShards[i])
                        {
                            data.StringStore[kvp.Key] = kvp.Value;
                        }
                    }

                    // Copy list store from all shards
                    for (int i = 0; i < ShardCount; i++)
                    {
                        listStoreLocks[i].EnterReadLock();
                        try
                        {
                            foreach (var kvp in listStoreShards[i])
                            {
                                data.ListStore[kvp.Key] = [.. kvp.Value];
                            }
                        }
                        finally
                        {
                            listStoreLocks[i].ExitReadLock();
                        }
                    }

                    // Copy hash store from all shards
                    for (int i = 0; i < ShardCount; i++)
                    {
                        foreach (var kvp in hashStoreShards[i])
                        {
                            // Convert ConcurrentDictionary to Dictionary for serialization
                            var regularDict = new Dictionary<string, string>();
                            foreach (var fieldKvp in kvp.Value)
                            {
                                regularDict[fieldKvp.Key] = fieldKvp.Value;
                            }
                            data.HashStore[kvp.Key] = regularDict;
                        }
                    }

                    // Check if we have any data to back up
                    if (data.StringStore.Count > 0 || data.ListStore.Count > 0 || data.HashStore.Count > 0)
                    {
                        // Generate timestamp for the filename
                        string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
                        string backupFileName = $"{backupFilePrefix}_{timestamp}{backupFileExtension}";
                        string backupFilePath = Path.Combine(backupDirectory, backupFileName);

                        try
                        {
                            // Serialize and save data - use a temporary file first to avoid corruption
                            string tempFilePath = backupFilePath + ".tmp";
                            
                            // Replace JSON serialization with BinaryFormatter
                            using (var fileStream = new FileStream(tempFilePath, FileMode.Create))
                            {
                                using var binaryWriter = new BinaryWriter(fileStream);
                                // Write backup time
                                binaryWriter.Write(data.BackupTime.ToBinary());

                                // Write string store
                                binaryWriter.Write(data.StringStore.Count);
                                foreach (var kvp in data.StringStore)
                                {
                                    binaryWriter.Write(kvp.Key);
                                    binaryWriter.Write(kvp.Value);
                                }

                                // Write list store
                                binaryWriter.Write(data.ListStore.Count);
                                foreach (var kvp in data.ListStore)
                                {
                                    binaryWriter.Write(kvp.Key);
                                    binaryWriter.Write(kvp.Value.Count);
                                    foreach (var item in kvp.Value)
                                    {
                                        binaryWriter.Write(item);
                                    }
                                }

                                // Write hash store
                                binaryWriter.Write(data.HashStore.Count);
                                foreach (var kvp in data.HashStore)
                                {
                                    binaryWriter.Write(kvp.Key);
                                    binaryWriter.Write(kvp.Value.Count);
                                    foreach (var fieldKvp in kvp.Value)
                                    {
                                        binaryWriter.Write(fieldKvp.Key);
                                        binaryWriter.Write(fieldKvp.Value);
                                    }
                                }
                            }

                            // If successfully written, move to final location
                            if (File.Exists(tempFilePath))
                            {
                                // Delete the target file if it already exists
                                if (File.Exists(backupFilePath))
                                {
                                    File.Delete(backupFilePath);
                                }

                                // Rename temp to final
                                File.Move(tempFilePath, backupFilePath);

                                Console.WriteLine($"Backup completed at {DateTime.Now}: " +
                                    $"{data.StringStore.Count} string keys, " +
                                    $"{data.ListStore.Count} list keys, " +
                                    $"{data.HashStore.Count} hash keys");
                                Console.WriteLine($"Saved to: {backupFileName}");

                                // Rotate backup files if needed
                                RotateBackupFiles();
                            }
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine($"Error during backup file creation: {ex.Message}");
                        }
                    }
                    else
                    {
                        Console.WriteLine("No data to back up");
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"Error during backup: {ex.Message}");
                    Console.WriteLine(ex.StackTrace);
                }
            });
        }
        #endregion
    }
}