using System.Collections.Concurrent;

#pragma warning disable IDE0130 // Namespace does not match folder structure
namespace ArmaRAMDb
#pragma warning restore IDE0130 // Namespace does not match folder structure
{
    public class RAMDb(string ardbPath = null) : IDisposable
    {
        private readonly string _ardbPath = Path.Combine(Environment.CurrentDirectory, ardbPath ?? Main.DEFAULT_ARDB_PATH);
        public static readonly ConcurrentDictionary<string, string> _keyValues = new();
        public static readonly ConcurrentDictionary<string, ConcurrentDictionary<string, string>> _hashTables = new();
        public static readonly ConcurrentDictionary<string, List<string>> _lists = new();

        public static bool AutoBackupEnabled { get; set; } = false;
        public static int BackupFrequencyMinutes { get; set; } = 60;
        public static int MaxBackupsToKeep { get; set; } = 10;
        private static Timer _backupTimer;

        public void ImportFromBinary()
        {
            try
            {
                if (File.Exists(_ardbPath))
                {
                    using var stream = new FileStream(_ardbPath, FileMode.Open);
                    using var reader = new BinaryReader(stream);

                    int version = reader.ReadInt32();
                    if (version != 1)
                    {
                        Main.Log($"Unsupported ARDB format version: {version}", "warning");
                        return;
                    }

                    _keyValues.Clear();
                    _hashTables.Clear();
                    _lists.Clear();

                    // Read KeyValues
                    int keyValueCount = reader.ReadInt32();
                    for (int i = 0; i < keyValueCount; i++)
                    {
                        string key = reader.ReadString();
                        string value = reader.ReadString();
                        _keyValues.TryAdd(key, value);
                        Main.Log($"Loaded key-value: {key} = {value[..Math.Min(50, value.Length)]}...", "debug");
                    }

                    // Read HashTables
                    int tableCount = reader.ReadInt32();
                    for (int i = 0; i < tableCount; i++)
                    {
                        string tableName = reader.ReadString();
                        Main.Log($"Loading table: {tableName}", "debug");

                        var concurrentDict = new ConcurrentDictionary<string, string>();
                        int entryCount = reader.ReadInt32();

                        for (int j = 0; j < entryCount; j++)
                        {
                            string key = reader.ReadString();
                            string value = reader.ReadString();
                            concurrentDict.TryAdd(key, value);
                            Main.Log($"Loaded entry: {key} = {value[..Math.Min(50, value.Length)]}...", "debug");
                        }

                        _hashTables.TryAdd(tableName, concurrentDict);
                    }

                    // Read Lists
                    int listCount = reader.ReadInt32();
                    for (int i = 0; i < listCount; i++)
                    {
                        string listName = reader.ReadString();
                        Main.Log($"Loading list: {listName}", "debug");

                        var items = new List<string>();
                        int itemCount = reader.ReadInt32();

                        for (int j = 0; j < itemCount; j++)
                        {
                            string value = reader.ReadString();
                            items.Add(value);
                            Main.Log($"Loaded item: {value[..Math.Min(50, value.Length)]}...", "debug");
                        }

                        _lists.TryAdd(listName, items);
                    }

                    Main.Log("ARDB import complete", "debug");
                }
                else
                {
                    Main.Log($"ARDB file not found at path: {_ardbPath}. A new database will be created when data is saved.", "info");
                }
            }
            catch (Exception ex)
            {
                Main.Log($"Error during ARDB import: {ex.Message}", "error");
            }
        }

        public void ExportToBinary(bool createBackup = false)
        {
            try
            {
                Directory.CreateDirectory(Path.GetDirectoryName(_ardbPath));

                using (var stream = new FileStream(_ardbPath, FileMode.Create))
                using (var writer = new BinaryWriter(stream))
                {
                    writer.Write(1);

                    Utils.WriteDataToBinaryWriter(writer);
                }

                if (createBackup)
                {
                    string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
                    string backupDirectory = Path.Combine(Path.GetDirectoryName(_ardbPath), "backups");
                    string backupFileName = Path.GetFileNameWithoutExtension(_ardbPath) + "_" + timestamp + Path.GetExtension(_ardbPath);
                    string backupPath = Path.Combine(backupDirectory, backupFileName);

                    Directory.CreateDirectory(backupDirectory);

                    using (var stream = new FileStream(backupPath, FileMode.Create))
                    using (var writer = new BinaryWriter(stream))
                    {
                        Utils.WriteDataToBinaryWriter(writer);
                    }

                    Main.Log($"Created backup at: {backupPath}", "debug");
                }

                Main.Log("ARDB export complete", "debug");
            }
            catch (Exception ex)
            {
                Main.Log($"Error during ARDB export: {ex.Message}", "error");
            }
        }

        public List<string> ListBackups()
        {
            string backupDirectory = Path.Combine(Path.GetDirectoryName(_ardbPath), "backups");
            List<string> backups = [];

            if (Directory.Exists(backupDirectory))
            {
                backups = [.. Directory.GetFiles(backupDirectory, "*.ardb").OrderByDescending(file => file)];
            }

            return backups;
        }

        public static bool RestoreFromBackup(string backupPath)
        {
            if (File.Exists(backupPath))
            {
                try
                {
                    using var stream = new FileStream(backupPath, FileMode.Open);
                    using var reader = new BinaryReader(stream);

                    int version = reader.ReadInt32();
                    if (version != 1)
                    {
                        Main.Log($"Unsupported ARDB format version in backup: {version}", "warning");
                        return false;
                    }

                    _keyValues.Clear();
                    _hashTables.Clear();
                    _lists.Clear();

                    Utils.ReadDataFromBinaryReader(reader);

                    Main.Log($"Restored from backup: {backupPath}", "info");
                    return true;
                }
                catch (Exception ex)
                {
                    Main.Log($"Failed to restore from backup: {ex.Message}", "error");
                }
            }

            return false;
        }

        public static void InitializeAutoBackup()
        {
            if (AutoBackupEnabled)
            {
                _backupTimer?.Dispose();
                
                _backupTimer = new Timer(BackupTimerCallback, null, 
                    TimeSpan.FromMinutes(BackupFrequencyMinutes),
                    TimeSpan.FromMinutes(BackupFrequencyMinutes));
                
                Main.Log($"Automatic backup initialized (every {BackupFrequencyMinutes} minutes)", "info");
            }
        }

        private static void BackupTimerCallback(object state)
        {
            try
            {
                Main.db.ExportToBinary(true);

                ManageBackupRotation();
                
                Main.Log($"Automatic backup created at {DateTime.Now}", "info");
            }
            catch (Exception ex)
            {
                Main.Log($"Automatic backup failed: {ex.Message}", "error");
            }
        }

        private static void ManageBackupRotation()
        {
            try
            {
                var backups = Main.db.ListBackups();

                if (backups.Count > MaxBackupsToKeep)
                {
                    for (int i = MaxBackupsToKeep; i < backups.Count; i++)
                    {
                        File.Delete(backups[i]);
                        Main.Log($"Deleted old backup: {backups[i]}", "info");
                    }
                }
            }
            catch (Exception ex)
            {
                Main.Log($"Backup rotation failed: {ex.Message}", "error");
            }
        }

        public void Dispose()
        {
            try
            {
                ExportToBinary(true);
                _backupTimer?.Dispose();
                Main.Log("RAMDb disposed with final save and backup", "action");
            }
            catch (Exception ex)
            {
                Main.Log($"Error during final save on disposal: {ex.Message}", "error");
            }
            GC.SuppressFinalize(this);
        }
    }
}