using System;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Microsoft.Maui.Dispatching;
using Microsoft.AspNetCore.SignalR.Client;

namespace RelayClient;

public static class NativeWebRtc
{
    [DllImport("webrtc_wrapper.dll")]
    public static extern IntPtr CreatePeerConnection();

    [DllImport("webrtc_wrapper.dll")]
    public static extern string CreateOffer(IntPtr pc);

    [DllImport("webrtc_wrapper.dll")]
    public static extern string CreateAnswer(IntPtr pc);

    [DllImport("webrtc_wrapper.dll")]
    public static extern void SetLocalDescription(IntPtr pc, string type, string sdp);

    [DllImport("webrtc_wrapper.dll")]
    public static extern void SetRemoteDescription(IntPtr pc, string type, string sdp);

    [DllImport("webrtc_wrapper.dll")]
    public static extern void AddIceCandidate(IntPtr pc, string candidate);
}

public enum RTCSdpType { Offer, Answer }

public class RTCSessionDescription
{
    public RTCSdpType Type { get; set; }
    public string Sdp { get; set; }
}

public class RTCIceCandidate
{
    public string Candidate { get; set; }
}

public class PeerConnection
{
    private readonly IntPtr _nativeHandle;
    public string RemoteId { get; set; }

    public PeerConnection()
    {
        _nativeHandle = NativeWebRtc.CreatePeerConnection();
    }

    public Task CreateOffer(Action<RTCSessionDescription> onOfferCreated)
    {
        var sdp = NativeWebRtc.CreateOffer(_nativeHandle);
        onOfferCreated?.Invoke(new RTCSessionDescription { Type = RTCSdpType.Offer, Sdp = sdp });
        return Task.CompletedTask;
    }

    public Task CreateAnswer(Action<RTCSessionDescription> onAnswerCreated)
    {
        var sdp = NativeWebRtc.CreateAnswer(_nativeHandle);
        onAnswerCreated?.Invoke(new RTCSessionDescription { Type = RTCSdpType.Answer, Sdp = sdp });
        return Task.CompletedTask;
    }

    public Task SetLocalDescription(RTCSessionDescription desc)
    {
        NativeWebRtc.SetLocalDescription(_nativeHandle, desc.Type.ToString(), desc.Sdp);
        return Task.CompletedTask;
    }

    public Task SetRemoteDescription(RTCSessionDescription desc)
    {
        NativeWebRtc.SetRemoteDescription(_nativeHandle, desc.Type.ToString(), desc.Sdp);
        return Task.CompletedTask;
    }

    public Task AddIceCandidate(RTCIceCandidate candidate)
    {
        NativeWebRtc.AddIceCandidate(_nativeHandle, candidate.Candidate);
        return Task.CompletedTask;
    }
}
public class WebRtcClient
{
    private readonly PeerConnection _peerConnection = new();
    private readonly HubConnection _signal;
    private string _myId;

    public WebRtcClient(string serverUrl)
    {
        _signal = new HubConnectionBuilder()
            .WithUrl($"{serverUrl}/webrtc")
            .WithAutomaticReconnect()
            .Build();

        _signal.On<string, string>("ReceiveOffer", (fromId, sdp) =>
        {
            MainThread.BeginInvokeOnMainThread(async () =>
            {
                await HandleOffer(fromId, sdp);
            });
        });

        _signal.On<string, string>("ReceiveAnswer", (fromId, sdp) =>
        {
            MainThread.BeginInvokeOnMainThread(async () =>
            {
                await HandleAnswer(sdp);
            });
        });

        _signal.On<string, string>("ReceiveIceCandidate", (fromId, candidate) =>
        {
            MainThread.BeginInvokeOnMainThread(async () =>
            {
                await HandleIceCandidate(candidate);
            });
        });
    }

    public async Task ConnectAsync()
    {
        await _signal.StartAsync();
        _myId = _signal.ConnectionId;
    }

    public async Task CallAsync(string targetId)
    {
        _peerConnection.RemoteId = targetId;
        await _peerConnection.CreateOffer(async offer =>
        {
            await _peerConnection.SetLocalDescription(offer);
            await _signal.InvokeAsync("SendOffer", targetId, offer.Sdp);
        });
    }

    public async Task HandleOffer(string fromId, string sdp)
    {
        _peerConnection.RemoteId = fromId;
        var remoteDesc = new RTCSessionDescription { Type = RTCSdpType.Offer, Sdp = sdp };
        await _peerConnection.SetRemoteDescription(remoteDesc);

        await _peerConnection.CreateAnswer(async answer =>
        {
            await _peerConnection.SetLocalDescription(answer);
            await _signal.InvokeAsync("SendAnswer", fromId, answer.Sdp);
        });
    }

    public async Task HandleAnswer(string sdp)
    {
        var remoteDesc = new RTCSessionDescription { Type = RTCSdpType.Answer, Sdp = sdp };
        await _peerConnection.SetRemoteDescription(remoteDesc);
    }

    public async Task HandleIceCandidate(string candidate)
    {
        await _peerConnection.AddIceCandidate(new RTCIceCandidate { Candidate = candidate });
    }

    public async Task SendIceCandidate(string candidate)
    {
        if (!string.IsNullOrEmpty(_peerConnection.RemoteId))
        {
            await _signal.InvokeAsync("SendIceCandidate", _peerConnection.RemoteId, candidate);
        }
    }
}