using Hcs.WebApp.Data.Hcs;
using Hcs.WebApp.Services;
using Hcs.WebApp.Utils;

namespace Hcs.WebApp.BackgroundServices.CampaignManagers
{
    public abstract class ManagerBase(
        IServiceScopeFactory scopeFactory,
        OperationExecutionState operationExecutionState,
        ResultGetState resultGetState,
        Campaign campaign) : IManager
    {
        protected readonly IServiceScope scope = scopeFactory.CreateScope();
        protected readonly OperationExecutionState operationExecutionState = operationExecutionState;
        protected readonly ResultGetState resultGetState = resultGetState;
        protected readonly Campaign campaign = campaign;

        private HeadquartersService? headquartersService;

        public Campaign Campaign => campaign;

        public IManager.ManagerState State { get; protected set; }

        protected HeadquartersService HeadquartersService => headquartersService ??= scope.ServiceProvider.GetRequiredService<HeadquartersService>();

        protected abstract int StepCount { get; }

        public event IManager.CampaignStarted OnCampaignStarted;
        public event IManager.CampaignProgressStep OnCampaignProgressStep;
        public event IManager.CampaignEnded OnCampaignEnded;

        public abstract Task ProcessAsync();

        public async Task EndWithFailAsync(Exception e)
        {
            var endedAt = DateTime.UtcNow;
            var failureReason = e.CombineMessages();
            await HeadquartersService.SetCampaignEndedWithFailAsync(campaign.Id, endedAt, failureReason);

            State = IManager.ManagerState.Ended;

            InvokeOnCampaignEnded(endedAt, failureReason);
        }

        protected void InvokeOnCampaignStarted(DateTime startedAt)
        {
            OnCampaignStarted?.Invoke(campaign.Id, startedAt);
        }

        protected async Task ProgressStepAsync(int step)
        {
            using var context = HeadquartersService.GetNewContext();
            await ProgressStepAsync(context, step);
        }

        protected void InvokeOnCampaignEnded(DateTime endedAt, string failureReason)
        {
            OnCampaignEnded?.Invoke(campaign.Id, campaign.Type, endedAt, failureReason);
        }

        protected async Task ProgressStepAsync(HcsDbContext context, int step)
        {
            campaign.Step = step;
            campaign.Progress = Convert.ToInt32(((step + 1) / (float)StepCount * 100));
            await HeadquartersService.UpdateCampaignStepAndProgressAsync(context, campaign);

            OnCampaignProgressStep?.Invoke(campaign.Id, campaign.Step, campaign.Progress);
        }
    }
}
