using Insight.Domain.Interfaces;
using Insight.Domain.Network;
using Insight.Domain.Network.Agent.Messages;
using Insight.Infrastructure.Entities;
using Insight.Server.Network.Agent;
using Insight.Server.Network.Web;
using Microsoft.Extensions.Logging;
using MongoDB.Driver;
using Vaitr.Network;

namespace Insight.Server.Network.Globals;

public class ProxyHandler : IMessageHandler<AgentSession>, IMessageHandler<WebSession>
{
    private readonly ISessionPool<AgentSession, IMessage> _agentPool;
    private readonly ISessionPool<WebSession, IMessage> _webPool;
    private readonly IMongoDatabase _database;
    private readonly ILogger<ProxyHandler> _logger;

    public ProxyHandler(
        ISessionPool<AgentSession, IMessage> agentPool,
        ISessionPool<WebSession, IMessage> webPool,
        IMongoDatabase database,
        ILogger<ProxyHandler> logger)
    {
        _agentPool = agentPool;
        _webPool = webPool;
        _database = database;
        _logger = logger;
    }

    public async ValueTask HandleAsync<TMessage>(WebSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage
    {
        switch (message)
        {
            case Proxy<Request> proxyRequest:
                await OnProxyRequestAsync(sender, proxyRequest, cancellationToken);
                break;
        }
    }

    public async ValueTask HandleAsync<TMessage>(AgentSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IMessage
    {
        switch (message)
        {
            case Proxy<Response> proxyResponse:
                await OnProxyResponseAsync(sender, proxyResponse, cancellationToken);
                break;
        }
    }

    private async ValueTask OnProxyRequestAsync(WebSession session, Proxy<Request> request, CancellationToken cancellationToken)
    {
        // get host
        var hostEntity = await _database.Host()
            .Find(Builders<HostEntity>
            .Filter
            .Eq(p => p.Id, request.ProxyId))
            .FirstOrDefaultAsync(cancellationToken);

        if (hostEntity is null)
        {
            _logger.LogWarning("hostEntity is null");
            return;
        }

        // get agent
        var agentEntity = await _database.Agent()
            .Find(Builders<AgentEntity>
            .Filter
            .Eq(p => p.Id, hostEntity.Agent))
            .FirstOrDefaultAsync(cancellationToken);

        if (agentEntity is null)
        {
            _logger.LogWarning("agentEntity is null");
            return;
        }

        // check if agent online
        if (_agentPool.FirstOrDefault(p => p.Value.Id == agentEntity.Id).Value is not AgentSession agent) return;

        // proxy-send request packet to agent
        await agent.SendAsync(request, cancellationToken);
    }

    private async ValueTask OnProxyResponseAsync(AgentSession session, Proxy<Response> response, CancellationToken cancellationToken)
    {
        // check if web online
        if (_webPool.FirstOrDefault().Value is not WebSession web) return;

        await web.SendAsync(response, cancellationToken);
    }
}