using System;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
using System.Diagnostics;

class Program
{
    static async void notify(string title, string message)
    {
        string command = $"notify-send '{title}' '{message}'";
        await ExecuteCommand(command);
    }

    static async Task Main(string[] args)
    {
        string username = Environment.GetEnvironmentVariable("SNEK_USERNAME") ?? "anonymous";
        string password = Environment.GetEnvironmentVariable("SNEK_PASSWORD") ?? "anonymous";

        while (true) // Loop for auto-reconnection
        {
            using (var client = new ClientWebSocket())
            {
                Uri serverUri = new Uri("wss://snek.molodetz.nl/rpc.ws");

                try
                {
                    await client.ConnectAsync(serverUri, CancellationToken.None);
                    Console.WriteLine("Connected to the server.");

                    var jsonMessage = JsonConvert.SerializeObject(new
                    {
                        method = "login",
                        args = new[] { username, password }
                    });

                    var bytes = Encoding.UTF8.GetBytes(jsonMessage);
                    var buffer = new ArraySegment<byte>(bytes);
                    await client.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None);
                    Console.WriteLine("Message sent: " + jsonMessage);

                    Boolean isLoggedIn = false;
                    var receiveBuffer = new byte[1024 * 1024]; // Optimized buffer size

                    while (client.State == WebSocketState.Open)
                    {
                        try
                        {
                            WebSocketReceiveResult result = await client.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), CancellationToken.None);
                            var responseMessage = Encoding.UTF8.GetString(receiveBuffer, 0, result.Count);
                            Console.WriteLine("Message received: " + responseMessage);

                            dynamic jsonResponse = JsonConvert.DeserializeObject(responseMessage);
                            if (!isLoggedIn)
                            {
                                Boolean isSuccess = jsonResponse.success != null ? (bool)jsonResponse.success : false;
                                if (!isSuccess)
                                {
                                    notify("Snek", $"Login as {username} failed");
                                    break;
                                }
                                notify("Snek", "Logged in as " + username);
                                isLoggedIn = true;
                                continue;
                            }
                            string receivedUsername = jsonResponse.username != null ? System.Security.SecurityElement.Escape((string)jsonResponse.username) : "Unknown User";
                            string message = jsonResponse.message != null ? System.Security.SecurityElement.Escape((string)jsonResponse.message) : "No message provided";

                            if (receivedUsername != username)
                            {
                                notify(receivedUsername, message);
                            }
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine("Error processing message: " + ex.Message);
                            break; // Exit the inner loop to reconnect
                        }
                    }
                }
                catch (System.InvalidOperationException ex)
                {
                    Console.WriteLine("Error connecting to server: " + ex.Message);
                }
                catch (Exception ex)
                {
                    Console.WriteLine("An error occurred: " + ex.Message);
                }

                // Wait before attempting to reconnect
                Console.WriteLine("Reconnecting in 5 seconds...");
                await Task.Delay(5000);
            }
        }
    }

    private static async Task ExecuteCommand(string command)
    {
        using (var process = new Process())
        {
            process.StartInfo.FileName = "bash";
            process.StartInfo.Arguments = "-c \"" + command + "\"";
            process.StartInfo.RedirectStandardOutput = true;
            process.StartInfo.UseShellExecute = false;
            process.StartInfo.CreateNoWindow = true;
            process.Start();
            await process.WaitForExitAsync();
        }
    }
}