using System.Text.Json;
using RelayClient.Crypto;
using RelayShared.Services;
using WebSocketSharp;

namespace RelayClient.Services;

public sealed class RelaySocketClient
{
    private readonly string _username;
    private readonly WebSocket _socket;

    public string? ServerPublicKey { get; private set; }

    public event Action<string>? RawMessageReceived;
    public event Action<SocketChannelList>? ChannelListReceived;
    public event Action<SocketEncryptedMessage>? EncryptedChatReceived;
    public event Action<SocketRtcSignalMessage>? EncryptedRtcSignalReceived;
    public event Action<string>? ServerPublicKeyReceived;
    public event Action<string>? Log;

    public RelaySocketClient(string username, string url = "ws://localhost:1337/")
    {
        _username = username;
        _socket = new WebSocket(url);
        _socket.OnMessage += OnMessage;
    }

    public void Connect()
    {
        _socket.Connect();

        var publicKey = KeyStorage.LoadPublicKey(_username);

        SendRaw($"REGISTER_KEY|{_username}|{publicKey}");
        SendRaw("GET_SERVER_KEY");
        SendRaw("GET_CHANNELS");
    }

    public void SendRaw(string message)
    {
        if (_socket.ReadyState == WebSocketState.Open)
            _socket.Send(message);
    }

    public void SendJson<T>(T payload)
    {
        SendRaw(JsonSerializer.Serialize(payload));
    }

    public void Disconnect()
    {
        _socket.OnMessage -= OnMessage;

        if (_socket.ReadyState == WebSocketState.Open)
            _socket.Close();
    }

    private void OnMessage(object? sender, MessageEventArgs e)
    {
        if (e.Data.StartsWith("SERVER:REGISTERED_KEY:"))
        {
            Log?.Invoke(e.Data);
            return;
        }

        RawMessageReceived?.Invoke(e.Data);
        Log?.Invoke($"[{_username}] RAW WS DATA: {e.Data}");

        try
        {
            using var doc = JsonDocument.Parse(e.Data);
            var root = doc.RootElement;

            if (!root.TryGetProperty("Type", out var typeElement))
                return;

            var type = (SignalType)typeElement.GetInt32();

            switch (type)
            {
                case SignalType.ChannelList:
                {
                    var channelList = JsonSerializer.Deserialize<SocketChannelList>(e.Data);
                    if (channelList is not null)
                        ChannelListReceived?.Invoke(channelList);

                    return;
                }

                case SignalType.ServerPublicKey:
                {
                    var serverKeyMessage = JsonSerializer.Deserialize<ServerPublicKeyMessage>(e.Data);
                    if (serverKeyMessage is not null)
                    {
                        ServerPublicKey = serverKeyMessage.PublicKey;
                        ServerPublicKeyReceived?.Invoke(serverKeyMessage.PublicKey);
                    }

                    return;
                }

                case SignalType.EncryptedSignal:
                {
                    var payload = JsonSerializer.Deserialize<SocketRtcSignalMessage>(e.Data);
                    if (payload is not null)
                        EncryptedRtcSignalReceived?.Invoke(payload);

                    return;
                }

                case SignalType.EncryptedChat:
                {
                    var payload = JsonSerializer.Deserialize<SocketEncryptedMessage>(e.Data);
                    if (payload is not null)
                        EncryptedChatReceived?.Invoke(payload);

                    return;
                }
            }
        }
        catch (Exception ex)
        {
            Log?.Invoke($"[{_username}] failed to process websocket message: {ex.Message}");
        }
    }
}