using System.Net.Sockets;
using System.Text;

#pragma warning disable IDE0130 // Namespace does not match folder structure
namespace ArmaDragonflyClient
#pragma warning restore IDE0130 // Namespace does not match folder structure
{
    internal class DragonflyClient(string host, int port, string password) : IDisposable
    {
        private TcpClient _client;
        private StreamReader _reader;
        private StreamWriter _writer;

        public async Task ConnectAsync()
        {
            if (_client?.Connected != true)
            {
                _client = new TcpClient();
                await _client.ConnectAsync(host, port);
                _reader = new StreamReader(_client.GetStream(), Encoding.ASCII);
                _writer = new StreamWriter(_client.GetStream(), Encoding.ASCII) { AutoFlush = true };

                if (!string.IsNullOrEmpty(password))
                {
                    if (await SendCommandAsync($"AUTH {password}") != "OK")
                        throw new InvalidOperationException("Invalid password provided.");
                }

                Main.Log("Connected to DragonflyDB.", "debug");
            }
        }

        public async Task<string> SendCommandAsync(string command, bool convertFromBase64 = false)
        {
            if (_client?.Connected != true)
                await ConnectAsync();

            await _writer!.WriteLineAsync(command);
            return ParseResponse(await _reader!.ReadLineAsync(), _reader, convertFromBase64);
        }

        public async Task<string> ReceiveMessageAsync()
        {
            string response = await _reader.ReadLineAsync();
            return ParseResponse(response, _reader);
        }

        private static string ParseResponse(string response, StreamReader reader, bool convertFromBase64 = false)
        {
            if (response == null)
                throw new InvalidOperationException("Null response received.");

            switch (response[0])
            {
                case '$':
                    int count = int.Parse(response[1..]);
                    if (count == -1)
                        return null!;

                    char[] buffer = new char[count];
                    reader.Read(buffer, 0, count);
                    reader.ReadLine();
                    return new string(buffer);

                case '*':
                    int arrayLength = int.Parse(response[1..]);
                    if (arrayLength == -1) return null!;

                    var elements = new List<string>();
                    for (int i = 0; i < arrayLength; i++)
                    {
                        string element = ParseResponse(reader.ReadLine(), reader, convertFromBase64);

                        if (convertFromBase64)
                        {
                            byte[] data = Convert.FromBase64String(element!);
                            string originalString = Encoding.UTF8.GetString(data);
                            elements.Add(originalString);
                        }
                        else
                        {
                            elements.Add(element!);
                        }
                    }

                    var output = elements.Count > 0
                        ? $"[{string.Join(",", elements.Select(e => e.StartsWith('[') ? e : $"\"{e}\""))}]"
                        : "";
                    return output;

                case '+':
                case '-':
                    return response[1..];

                case ':':
                    return response[1..].Trim();

                default:
                    throw new NotSupportedException($"Unsupported response type: {response}");
            }
        }

        public void Disconnect()
        {
            _writer?.Close();
            _reader?.Close();
            _client?.Close();

            _writer?.Dispose();
            _reader?.Dispose();
            _client?.Dispose();

            _writer = null;
            _reader = null;
            _client = null;

            Main.Log("Disconnected from DragonflyDB.", "debug");
        }

        public void Dispose()
        {
            Disconnect();
            GC.SuppressFinalize(this);
        }
    }
}