using System.Text.Json;
using System.Text.Json.Serialization;
using RelayClient.Crypto;
using RelayShared.Rtc;
using RelayShared.Services;

namespace RelayClient.Services;

public sealed class RtcBridgeService
{
    private readonly string _username;
    private readonly RelaySocketClient _socket;
    private readonly HybridWebView _hybridWebView;
    private readonly Func<string?> _getCurrentChannelId;
    private readonly Action<string> _sendRawToWebView;

    public RtcBridgeService(string username, RelaySocketClient socket, HybridWebView hybridWebView,
                            Func<string?> getCurrentChannelId, Action<string> sendRawToWebView)
    {
        _username = username;
        _socket = socket;
        _hybridWebView = hybridWebView;
        _getCurrentChannelId = getCurrentChannelId;
        _sendRawToWebView = sendRawToWebView;
    }

    public Task JoinRtcChannel()
    {
        var channelId = _getCurrentChannelId();

        if (string.IsNullOrWhiteSpace(channelId))
            return Task.CompletedTask;

        _socket.SendRaw($"RTC_JOIN_CHANNEL|{_username}|{channelId}");
        return Task.CompletedTask;
    }

    public void LeaveRtcChannel()
    {
        var channelId = _getCurrentChannelId();

        if (string.IsNullOrWhiteSpace(channelId))
            return;

        _socket.SendRaw($"RTC_LEAVE_CHANNEL|{_username}|{channelId}");
    }

    public void SendRtcSignal(string json)
    {
        if (string.IsNullOrWhiteSpace(_socket.ServerPublicKey))
        {
            _sendRawToWebView("SendRtcSignal failed: server public key not loaded.");
            return;
        }

        RtcSignalMessage? rtcSignal;

        try
        {
            rtcSignal = JsonSerializer.Deserialize<RtcSignalMessage>(json);
        }
        catch (Exception ex)
        {
            _sendRawToWebView("SendRtcSignal failed to parse RTC signal: " + ex.Message);
            return;
        }

        if (rtcSignal is null)
            return;

        rtcSignal.ChannelId ??= _getCurrentChannelId();
        rtcSignal.From ??= _username;

        // _sendRawToWebView($"RTC_SIGNAL file: {JsonSerializer.Serialize(rtcSignal)}");
        if (string.IsNullOrWhiteSpace(rtcSignal.ChannelId))
        {
            _sendRawToWebView("SendRtcSignal failed: missing channel id.");
            return;
        }

        var outgoingJson = JsonSerializer.Serialize(rtcSignal);

        try
        {
            var encrypted = E2EeHelper.EncryptForRecipient(outgoingJson, _socket.ServerPublicKey);

            var payload = new SocketRtcSignalMessage
            {
                Type = SignalType.EncryptedSignal,
                SenderUsername = _username,
                ChannelId = rtcSignal.ChannelId,
                CipherText = encrypted.CipherText,
                Nonce = encrypted.Nonce,
                Tag = encrypted.Tag,
                EncryptedKey = encrypted.EncryptedKey
            };

            _socket.SendJson(payload);

            _sendRawToWebView($"SendRtcSignal sent: {rtcSignal.Type} -> {rtcSignal.To}");
        }
        catch (Exception ex)
        {
            _sendRawToWebView("SendRtcSignal failed: " + ex.Message);
        }
    }

    public async Task<string> GetRtcParticipants()
    {
        var channelId = _getCurrentChannelId();

        if (string.IsNullOrWhiteSpace(channelId))
            return "[]";

        var participants = await ServerAPI.GetRtcParticipantsAsync(channelId);
        return JsonSerializer.Serialize(participants ?? []);
    }

    public async Task HandleIncomingRtcSignalAsync(SocketRtcSignalMessage payload)
    {
        // _sendRawToWebView("HandleIncomingRtcSignal called");
        var currentChannelId = _getCurrentChannelId();

        if (payload.ChannelId != currentChannelId)
        {
            _sendRawToWebView("Channel id does not match");
            return;
        }

        if (payload.SenderUsername == _username)
        {
            _sendRawToWebView("Received own message");
            return;
        }

        string decryptedJson;
        
        try
        {
            var privateKey = KeyStorage.LoadPrivateKey(_username);

            decryptedJson = E2EeHelper.DecryptForRecipient(
                new EncryptedPayload
                {
                    CipherText = payload.CipherText,
                    Nonce = payload.Nonce,
                    Tag = payload.Tag,
                    EncryptedKey = payload.EncryptedKey
                },
                privateKey
            );
        }
        catch (Exception ex)
        {
            _sendRawToWebView("RTC decrypt failed: " + ex.Message);
            return;
        }

        RtcSignalMessage? rtcSignal;

        try
        {
            rtcSignal = JsonSerializer.Deserialize<RtcSignalMessage>(decryptedJson);
            // _sendRawToWebView($"Received Encrypted Signal: [{rtcSignal.From}]: {rtcSignal.Offer}");
        }
        catch (Exception ex)
        {
            _sendRawToWebView("RTC signal parse failed: " + ex.Message);
            return;
        }

        if (rtcSignal is null)
        {
            _sendRawToWebView("rtcSignal is null");
            return;
        }

        if (!string.IsNullOrWhiteSpace(rtcSignal.To) &&
            !string.Equals(rtcSignal.To, _username, StringComparison.OrdinalIgnoreCase))
        {
            _sendRawToWebView($"Ignoring RTC signal meant for {rtcSignal.To}");
            return;
        }

        // _sendRawToWebView("Received encrypted RTC signal: " + decryptedJson);

        await SendRtcSignalToJsAsync(rtcSignal);
    }

    public Task PushRtcContextToJsAsync()
    {
        MainThread.BeginInvokeOnMainThread(async () =>
        {
            var usernameJson = JsonSerializer.Serialize(_username);
            var channelIdJson = JsonSerializer.Serialize(_getCurrentChannelId());

            await _hybridWebView.EvaluateJavaScriptAsync($"window.setUsername({usernameJson})");
            await _hybridWebView.EvaluateJavaScriptAsync($"window.setChannelId({channelIdJson})");
        });

        return Task.CompletedTask;
    }

    private Task SendRtcSignalToJsAsync(RtcSignalMessage data)
    {
        if (data.Type == "rtc_offer" || data.Type == "rtc_answer")
        {
            data.Sdp = data.Sdp.Replace("\r\n", "(rn)");
        }
        MainThread.BeginInvokeOnMainThread(async () =>
        {
            try
            {
                // await _hybridWebView.InvokeJavaScriptAsync("testIndex", [JsonSerializer.Serialize(data)], [RtcJsType.Default.String]);
                await _hybridWebView.InvokeJavaScriptAsync("testIndex", [data], [RtcJsType.Default.RtcSignalMessage]);
                #region OldDebugger
//                  var jsArg = JsonSerializer.Serialize(data);
//
//                 await _hybridWebView.EvaluateJavaScriptAsync($@"
//                     try {{
//                         window.HybridWebView.SendRawMessage('C# eval entered');
//
//                         if (!window.RelaySocket) {{
//                             window.HybridWebView.SendRawMessage('window.RelaySocket missing');
//                         }} else if (typeof window.RelaySocket.receiveRtcSignal !== 'function') {{
//                             window.HybridWebView.SendRawMessage('RelaySocket.receiveRtcSignal missing');
//                         }} else {{
//                             window.HybridWebView.SendRawMessage('Calling RelaySocket.receiveRtcSignal');
//                             window.RelaySocket.receiveRtcSignal({jsArg});
//                         }}
//                     }} catch (err) {{
//                         window.HybridWebView.SendRawMessage('RTC JS dispatch failed: ' + err);
//                     }}
//                 ");
                #endregion  
            }
            catch (Exception ex)
            {
                _sendRawToWebView("SendRtcSignalToJsAsync failed: " + ex.Message);
            }
        });
        return Task.CompletedTask;
    }
}

[JsonSourceGenerationOptions(WriteIndented = false)]
[JsonSerializable(typeof(RtcDescription))]
[JsonSerializable(typeof(List<RtcSignalMessage>))]
[JsonSerializable(typeof(RtcSignalMessage))]
[JsonSerializable(typeof(IceCandidate))]
[JsonSerializable(typeof(List<IceCandidate>))]
[JsonSerializable(typeof(string))]
internal partial class RtcJsType : JsonSerializerContext
{
}