using System.Text;
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
{
    internal class Utils
    {
        public static string Base64Decode(string encodedString)
        {
            byte[] data = Convert.FromBase64String(encodedString);
            return Encoding.UTF8.GetString(data);
        }

        public static string Base64Encode(string plainText)
        {
            byte[] data = Encoding.UTF8.GetBytes(plainText);
            return Convert.ToBase64String(data);
        }

        public static string BuildArray(string text)
        {
            string str = $"[\"{text}\"]";
            return str;
        }

        public static List<string> SplitIntoChunks(string data, int chunkSize)
        {
            int chunksNeeded = (int)Math.Ceiling((double)data.Length / chunkSize);
            List<string> chunks = [];

            for (int i = 0; i < chunksNeeded; i++)
            {
                int start = i * chunkSize;
                int end = Math.Min(data.Length, start + chunkSize);
                chunks.Add(data[start..end]);
            }

            return chunks;
        }

        public static long GetUniqueId()
        {
            return DateTimeOffset.Now.ToUnixTimeMilliseconds();
        }

        public static void WriteDataToBinaryWriter(BinaryWriter writer)
        {
            // Write KeyValues
            writer.Write(RAMDb._keyValues.Count);
            foreach (var pair in RAMDb._keyValues)
            {
                writer.Write(pair.Key);
                writer.Write(pair.Value);
            }

            // Write HashTables
            writer.Write(RAMDb._hashTables.Count);
            foreach (var table in RAMDb._hashTables)
            {
                writer.Write(table.Key);
                writer.Write(table.Value.Count);

                foreach (var entry in table.Value)
                {
                    writer.Write(entry.Key);
                    writer.Write(entry.Value);
                }
            }

            // Write Lists
            writer.Write(RAMDb._lists.Count);
            foreach (var list in RAMDb._lists)
            {
                writer.Write(list.Key);
                writer.Write(list.Value.Count);

                foreach (var item in list.Value)
                {
                    writer.Write(item);
                }
            }
        }

        public static void ReadDataFromBinaryReader(BinaryReader reader)
        {
            // Read KeyValues
            int keyValueCount = reader.ReadInt32();
            for (int i = 0; i < keyValueCount; i++)
            {
                string key = reader.ReadString();
                string value = reader.ReadString();
                RAMDb._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");
                }

                RAMDb._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");
                }

                RAMDb._lists.TryAdd(listName, items);
            }

            Main.Log("ARDB import complete", "debug");
        }

        public static void CheckByteCount(string uniqueId, string data, string function, string entity, bool call, int bufferSize)
        {
            if (Encoding.UTF8.GetByteCount(data) <= bufferSize)
            {
                if (!data.StartsWith('['))
                    data = BuildArray(data);

                Main.Log($"Single chunk data: {data}", "debug");

                string dataAsString = $"[\"{uniqueId}\",\"{function}\",{call.ToString().ToLower()},{data},\"{entity}\"]";

                Main.Log($"Single chunk data string: {dataAsString}", "debug");
                Main.Callback("ArmaRAMDb", "ramdb_db_fnc_handler", dataAsString);
            }
            else
            {
                if (!data.StartsWith('[') || !data.EndsWith(']'))
                    data = BuildArray(data);

                Main.Log($"Single chunk data: {data}", "debug");

                var chunks = SplitIntoChunks(data, bufferSize);
                int totalChunks = chunks.Count;

                for (int chunkIndex = 0; chunkIndex < chunks.Count; chunkIndex++)
                {
                    string escapedChunkData = chunks[chunkIndex].Replace("\"", "\"\"");
                    string chunkAsString = $"[\"{uniqueId}\",\"{function}\",{chunkIndex+1},{totalChunks},\"{escapedChunkData}\",{call.ToString().ToLower()},\"{entity}\"]";

                    Main.Log($"Sending chunk {chunkIndex+1}/{totalChunks}: {chunkAsString}", "debug");
                    Main.Callback("ArmaRAMDb", "ramdb_db_fnc_fetch", chunkAsString);
                }
            }
        }
    }
}