using Amazon.Runtime.Internal;
using Insight.Domain.Network;
using Insight.Domain.Network.Agent.Messages;
using Insight.Infrastructure.Entities;
using Insight.Server.Extensions;
using Insight.Server.Network.Agent;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using MongoDB.Driver;
using Vaitr.Network;

namespace Insight.Server.Services;

internal class JobService : BackgroundService
{
    private readonly ISessionPool<AgentSession, IMessage> _agentPool;
    private readonly IMongoDatabase _database;
    private readonly ILogger<JobService> _logger;

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

    protected override async Task ExecuteAsync(CancellationToken cancellationToken)
    {
        _logger.LogTrace("ExecuteAsync");

        var jobs = new List<Task>
        {
            RunInventoryAsync(cancellationToken),
            //RunCustomsAsync(cancellationToken)
        };

        try
        {
            await Task.WhenAll(jobs).ConfigureAwait(false);
        }
        catch (OperationCanceledException) { }
        catch (Exception) { }
    }

    private async Task RunInventoryAsync(CancellationToken cancellationToken)
    {
        _logger.LogTrace("RunInventoryAsync");

        while (cancellationToken.IsCancellationRequested is false)
        {
            try
            {
                foreach (var agent in await GetAssignedAgentsAsync(cancellationToken))
                {
                    await agent.SendAsync(new InventoryRequest(), cancellationToken);
                }
            }
            catch (OperationCanceledException) { }
            catch (Exception) { }
            finally
            {
                await Task.Delay(TimeSpan.FromHours(1), cancellationToken);
            }
        }
    }

    private async Task RunCustomsAsync(CancellationToken cancellationToken)
    {
        _logger.LogTrace("RunCustomsAsync");

        while (cancellationToken.IsCancellationRequested is false)
        {
            try
            {
                // check if agent online
                if (_agentPool.FirstOrDefault().Value is not AgentSession agent) continue;

                // proxy-send request packet to agent
                await agent.SendAsync(new Request
                {
                    RequestId = "test",
                    RequestData = "Get-Date"
                }, cancellationToken);
            }
            catch (OperationCanceledException) { }
            catch (Exception) { }
            finally
            {
                await Task.Delay(TimeSpan.FromSeconds(3), cancellationToken);
            }
        }

        
    }

    private async ValueTask<IEnumerable<AgentSession>> GetAssignedAgentsAsync(CancellationToken cancellationToken)
    {
        var valid = new List<AgentSession>();

        await Async.ParallelForEach(_agentPool.Where(p => p.Value.Id is not null), async x =>
        {
            var host = await _database.Host()
                .Find(Builders<HostEntity>.Filter.Eq(p => p.Agent, x.Value.Id))
                .FirstOrDefaultAsync(cancellationToken)
                .ConfigureAwait(false);

            if (host is null) return;
            valid.Add(x.Value);
        });

        return valid;
    }
}