using Hcs.Broker;
using Hcs.Broker.Logger;
using Hcs.Broker.MessageCapturer;
using Hcs.WebApp.BackgroundServices.ResultGetters;
using Hcs.WebApp.Config;
using Hcs.WebApp.Data.Hcs;
using Hcs.WebApp.Services;
using Hcs.WebApp.Utils;

namespace Hcs.WebApp.BackgroundServices
{
    public class ResultWaitService(
        ResultWaitState state,
        ResultGetterFactory resultGetterFactory,
        IServiceScopeFactory scopeFactory) : BackgroundService
    {
        private class WaitState
        {
            public int attempt;
            public int timer;
            public bool done;
        }

        private const int ITERATION_TIME = 10000;
        private const int SLEEP_TIME = 30000;

        private readonly ResultWaitState state = state;
        private readonly ResultGetterFactory resultGetterFactory = resultGetterFactory;
        private readonly IServiceScopeFactory scopeFactory = scopeFactory;
        private readonly List<(Operation operation, WaitState state)> entries = [];

        private Broker.Logger.ILogger logger;
        private IMessageCapturer messageCapturer;
        private IClient client;

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            await InitializeStateAsync();

            InitializeClient();

            while (!stoppingToken.IsCancellationRequested)
            {
                while (state.TryDequeueOperation(out var operation))
                {
                    state.SetProcessingOperation(operation);

                    if (stoppingToken.IsCancellationRequested) return;

                    entries.Add(new (operation, new WaitState()));
                }

                if (entries.Count > 0)
                {
                    await Task.Delay(ITERATION_TIME, stoppingToken);

                    var scope = scopeFactory.CreateScope();
                    foreach (var entry in entries)
                    {
                        entry.state.timer += ITERATION_TIME;

                        var send = entry.state.attempt switch
                        {
                            0 => entry.state.timer >= 10000,
                            1 => entry.state.timer >= 60000,
                            2 => entry.state.timer >= 300000,
                            3 => entry.state.timer >= 900000,
                            _ => entry.state.timer >= 1800000,
                        };
                        if (send)
                        {
                            try
                            {
                                var resultGetter = resultGetterFactory.CreateResultGetter(scope, client, entry.operation);
                                var success = await resultGetter.GetAsync();
                                if (success)
                                {
                                    entry.state.done = true;
                                }
                            }
                            catch (Exception e)
                            {
                                var headquartersService = scope.ServiceProvider.GetRequiredService<HeadquartersService>();
                                await headquartersService.SetOperationEndedWithFailAsync(entry.operation.Id, e.CombineMessages());

                                entry.state.done = true;
                            }

                            entry.state.attempt++;
                            entry.state.timer = 0;
                        }

                        if (entry.state.done)
                        {
                            state.UnsetProcessingOperation(entry.operation);
                        }
                    }

                    entries.RemoveAll(x => x.state.done);
                }
                else
                {
                    await Task.Delay(SLEEP_TIME, stoppingToken);
                }
            }
        }

        private async Task InitializeStateAsync()
        {
            using var scope = scopeFactory.CreateScope();
            var headquartersService = scope.ServiceProvider.GetRequiredService<HeadquartersService>();

            var operations = await headquartersService.GetResultWaitingOperationsAsync();
            foreach (var operation in operations)
            {
                state.EnqueueOperation(operation);
            }

            state.Ready = true;
        }

        private void InitializeClient()
        {
            logger = new ActionLogger();
            messageCapturer = new FileMessageCapturer("incoming", logger);

            using var scope = scopeFactory.CreateScope();
            var configuration = scope.ServiceProvider.GetRequiredService<IConfiguration>();
            var config = configuration.GetSection("BrokerConfig").Get<BrokerConfig>()!;
            var clientProvider = scope.ServiceProvider.GetRequiredService<IClientProvider>();
            client = clientProvider.CreateClient(config, logger, messageCapturer);
        }
    }
}
