using System.Net;
using System.Net.Sockets;
using System.Net.WebSockets;
using System.Text;
using System.Text.Json;
using InnovEnergy.App.Backend.Database;

namespace InnovEnergy.App.Backend.Websockets;

public static class WebsocketManager
{
    public static Dictionary<Int64, InstallationInfo> InstallationConnections = new Dictionary<Int64, InstallationInfo>();

    //Every 2 minutes, check the timestamp of the latest received message for every installation.
    //If the difference between the two timestamps is more than two minutes, we consider this installation unavailable.
    public static async Task MonitorInstallationTable()
    {
        while (true){
            lock (InstallationConnections){
                foreach (var installationConnection in InstallationConnections){
                    if ((DateTime.Now - installationConnection.Value.Timestamp) > TimeSpan.FromMinutes(1)){
                        installationConnection.Value.Status = -1;
                        if (installationConnection.Value.Connections.Count > 0){InformWebsocketsForInstallation(installationConnection.Key);}
                    }
                }
            }
            await Task.Delay(TimeSpan.FromMinutes(2));
        }
    }

    //Inform all the connected websockets regarding installation "installationId"
    public static void InformWebsocketsForInstallation(Int64 installationId)
    {
        var installation = Db.GetInstallationById(installationId);
        var installationConnection = InstallationConnections[installationId];
        Console.WriteLine("Update all the connected websockets for installation " + installationId);
        

        var jsonObject = new
        {
            id = installationId,
            status = installationConnection.Status,
            testingMode = installation.TestingMode
        };

        string jsonString = JsonSerializer.Serialize(jsonObject);
        byte[] dataToSend = Encoding.UTF8.GetBytes(jsonString);

        foreach (var connection in installationConnection.Connections)
        {
            connection.SendAsync(
                new ArraySegment<byte>(dataToSend, 0, dataToSend.Length),
                WebSocketMessageType.Text,
                true, // Indicates that this is the end of the message
                CancellationToken.None
            );
        }
    }

    public static async Task HandleWebSocketConnection(WebSocket currentWebSocket)
    {
        var buffer = new byte[4096];
        try
        {
            while (currentWebSocket.State == WebSocketState.Open)
            {
                //Listen for incoming messages on this WebSocket
                var result = await currentWebSocket.ReceiveAsync(buffer, CancellationToken.None);
                
                if (result.MessageType != WebSocketMessageType.Text)
                    continue;
                
                var message = Encoding.UTF8.GetString(buffer, 0, result.Count);
                var installationIds = JsonSerializer.Deserialize<int[]>(message);

                //This is a ping message to keep the connection alive, reply with a pong
                if (installationIds[0] == -1)
                {
                    var jsonObject = new
                    {
                        id = -1,
                        status = -1
                    };

                    var jsonString = JsonSerializer.Serialize(jsonObject); 
                    var dataToSend = Encoding.UTF8.GetBytes(jsonString);
                    currentWebSocket.SendAsync(dataToSend,
                        WebSocketMessageType.Text,
                        true, 
                        CancellationToken.None
                    );
                    
                    continue;
                }
               
                //Console.WriteLine("Received a new message from websocket");
                lock (InstallationConnections)
                {
                    //Each front-end will send the list of the installations it wants to access
                    //If this is a new key (installation id), initialize the list for this key and then add the websocket object for this client
                    //Then, report the status of each requested installation to the front-end that created the websocket connection
                    foreach (var installationId in installationIds)
                    {
                        //Console.WriteLine("New id is "+installationId);
                        var installation = Db.GetInstallationById(installationId);
                        if (!InstallationConnections.ContainsKey(installationId))
                        {
                            //Console.WriteLine("Create new empty list for installation id " + installationId);
                            InstallationConnections[installationId] = new InstallationInfo
                            {
                                Status = -1
                            };
                        }

                        InstallationConnections[installationId].Connections.Add(currentWebSocket);

                        var jsonObject = new
                        {
                            id = installationId,
                            status = InstallationConnections[installationId].Status,
                            testingMode = installation.TestingMode
                        };

                        var jsonString = JsonSerializer.Serialize(jsonObject);
                        var dataToSend = Encoding.UTF8.GetBytes(jsonString);


                        currentWebSocket.SendAsync(dataToSend,
                            WebSocketMessageType.Text,
                            true, // Indicates that this is the end of the message
                            CancellationToken.None
                        );
                    }

                    Console.WriteLine("Printing installation connection list");
                    Console.WriteLine("----------------------------------------------");
                    foreach (var installationConnection in InstallationConnections)
                    {
                        Console.WriteLine("Installation ID: " + installationConnection.Key + " Number of Connections: " + installationConnection.Value.Connections.Count);
                    }
                    Console.WriteLine("----------------------------------------------");
                }
            }

            lock (InstallationConnections)
            {
                //When the front-end terminates the connection, the following code will be executed
                Console.WriteLine("The connection has been terminated");
                foreach (var installationConnection in InstallationConnections)
                {
                    if (installationConnection.Value.Connections.Contains(currentWebSocket))
                    {
                        installationConnection.Value.Connections.Remove(currentWebSocket);
                    }
                }
            }

            await currentWebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Connection closed by server", CancellationToken.None);
            lock (InstallationConnections)
            {
                //Print the installationConnections dictionary after deleting a websocket
                Console.WriteLine("Print the installation connections list after deleting a websocket");
                Console.WriteLine("----------------------------------------------");
                foreach (var installationConnection in InstallationConnections)
                {
                    Console.WriteLine("Installation ID: " + installationConnection.Key + " Number of Connections: " + installationConnection.Value.Connections.Count);
                }

                Console.WriteLine("----------------------------------------------");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("WebSocket error: " + ex.Message);
        }
    }
    
}