using Insight.Domain.Enums;
using Insight.Domain.Interfaces;
using Insight.Domain.Network;
using Insight.Server.Network.Web;
using Microsoft.Extensions.Logging;
using Vaitr.Bus;
using Vaitr.Network;

namespace Insight.Server.Network.Remote;

public class RemoteSession : TcpSession<IMessage>
{
    public string Id { get; }
    public RemoteControlMode Mode { get; set; }

    private readonly Bus _bus;
    private readonly ISessionPool<WebSession, IMessage> _webPool;
    private readonly IEnumerable<IMessageHandler<RemoteSession>> _handlers;

    public RemoteSession(
        ISessionPool<WebSession, IMessage> webPool,
        IEnumerable<IMessageHandler<RemoteSession>> handlers,
        ISerializer<IMessage> serializer,
        ILogger<RemoteSession> logger) : base(serializer, logger)
    {
        Id = GenerateRandomId();

        _webPool = webPool;
        _handlers = handlers;
    }

    public async ValueTask ProxyAsync<TMessage>(TMessage message, CancellationToken cancellationToken)
        where TMessage : IMessage
    {
        // check if web online
        if (_webPool.FirstOrDefault().Value is not WebSession web) return;

        // proxy-send request packet to web
        await web.SendAsync(new Proxy<TMessage>
        {
            Message = message
        }, cancellationToken);
    }

    protected override async ValueTask OnConnectedAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("Remote ({ep?}) connected", RemoteEndPoint);

        if (_webPool.FirstOrDefault().Value is not WebSession webSession) return;
    }

    protected override async ValueTask OnDisconnectedAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("Remote ({ep?}) disconnected", RemoteEndPoint);
    }

    protected override async ValueTask OnSentAsync(IPacketContext<IMessage> context, CancellationToken cancellationToken)
    {
        await base.OnSentAsync(context, cancellationToken);
    }

    protected override async ValueTask OnReceivedAsync(IPacketContext<IMessage> context, CancellationToken cancellationToken)
    {
        await base.OnReceivedAsync(context, cancellationToken);

        foreach (var handler in _handlers)
        {
            try
            {
                await handler.HandleAsync(this, context.Packet, cancellationToken);
            }
            catch (Exception ex)
            {
                _logger.LogWarning("Remote ({ep?}) {ex}", RemoteEndPoint, ex.ToString());
            }
        }
    }

    protected override async ValueTask OnHeartbeatAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("Remote ({ep?}) Heartbeat", RemoteEndPoint);
    }

    private static string GenerateRandomId()
    {
        var random = new Random();
        string? sessionId = string.Empty;

        for (var i = 0; i < 3; i++) sessionId += random.Next(0, 999).ToString().PadLeft(3, '0');
        return sessionId;
    }
}