using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using System.Net;
using System.Net.Http;
using System.Net.WebSockets;

namespace EVCB_OCPP.WSServer.Service.WsService;

public class WebsocketService<T> where T : WsSession
{
    public WebsocketService(IServiceProvider serviceProvider)
    {
        this.serviceProvider = serviceProvider;
    }

    private readonly IServiceProvider serviceProvider;

    public event EventHandler<T> NewSessionConnected;

    public async Task AcceptWebSocket(HttpContext context)
    {
        if (!context.WebSockets.IsWebSocketRequest)
        {
            return;
        }

        var portocol = await ValidateSupportedPortocol(context);
        if (string.IsNullOrEmpty(portocol))
        {
            return;
        }

        T data = GetSession(context);

        if (!await ValidateHandshake(context, data))
        {
            return;
        }

        using WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync(portocol);
        await AddWebSocket(webSocket, data);
    }

    internal virtual ValueTask<bool> ValidateHandshake(HttpContext context, T data)
    {
        return ValueTask.FromResult(true);
    }

    internal virtual ValueTask<string> ValidateSupportedPortocol(HttpContext context)
    {
        return ValueTask.FromResult(string.Empty);
    }


    private async Task AddWebSocket(WebSocket webSocket, T data)
    {
        data.ClientWebSocket = webSocket;

        NewSessionConnected?.Invoke(this, data);
        await data.EndConnSemaphore.WaitAsync();
        return;
    }

    private T GetSession(HttpContext context)
    {
        T data = serviceProvider.GetRequiredService<T>();
        data.Path = context?.Request?.Path;
        data.SessionID = context.TraceIdentifier;
        data.UriScheme = GetScheme(context);

        try
        {
            var ipaddress = context.Connection.RemoteIpAddress;
            var port = context.Connection.RemotePort;
            data.Endpoint = new IPEndPoint(ipaddress, port);
        }
        catch
        {
            data.Endpoint = null;
        }

        return data;
    }

    private string GetScheme(HttpContext context)
    {
        string toReturn = string.Empty;

        if (context.Request.Headers.ContainsKey("x-original-host"))
        {
            toReturn = new Uri(context.Request.Headers["x-original-host"]).Scheme;
            return toReturn;
        }

        var origin = context.Request.Headers.Origin.FirstOrDefault();
        try
        {
            toReturn = new Uri(origin).Scheme;
            return toReturn;
        }
        catch
        {
        }

        return toReturn;
    }
}