using GB5Shared.GenerateAutoNumber; using GB5Shared.DTO.Framework.Login; using Microsoft.AspNetCore.SignalR; using MMBLL.MRPRun; using MMDAL.DTO.MRPRun; using MMSL.Hubs; using static GB5Shared.GB5Constant.Constant; namespace MMSL.Services { // Registered as Singleton — receives Dapr pub/sub messages on "mrprun.trigger" topic. // Uses IServiceScopeFactory because it needs scoped BLL services per message. public class MRPRunProcessingService { private readonly IServiceScopeFactory _ScopeFactory; private readonly IHubContext _HubContext; private readonly ILogger _Logger; public MRPRunProcessingService( IServiceScopeFactory scopeFactory, IHubContext hubContext, ILogger logger) { _ScopeFactory = scopeFactory; _HubContext = hubContext; _Logger = logger; } public async Task ProcessAsync( MRPRunTriggerDTO trigger,LoginDTO loginDTO, CancellationToken ct = default) { var group = MRPRunHub.HubGroup(trigger.WorkOUId); using var scope = _ScopeFactory.CreateScope(); var bll = scope.ServiceProvider.GetRequiredService(); //var login = new LoginDTO //{ // ClientId = trigger.TenantId, // WorkOUId = trigger.WorkOUId, // UserId = trigger.UserId, // DatabaseName = trigger.DatabaseName //}; var login = loginDTO; int mrpRunId = 0; try { mrpRunId = await bll.SaveMRPRun( new MRPRunDTO { MRPRunId = trigger.MrpRunId, MRPId = trigger.MrpId, MRPRunMRPDate = DateTime.UtcNow, MRPRunType = (byte)trigger.TypeofRun, RunScope = (byte)trigger.RunScope, ScopeObjectId = trigger.RunScope switch { MrpRunScope.Allocation => trigger.AllocationId ?? 0, MrpRunScope.SalesOrder => trigger.SalesOrderId ?? 0, MrpRunScope.FGItem => trigger.FGItemIds?.FirstOrDefault() ?? 0, _ => 0 }, TenantId = trigger.TenantId }, login).ConfigureAwait(false); var progress = new Progress(async report => { try { await _HubContext.Clients.Group(group) .ReceiveProgress( report.Percent, report.Step, report.CurrentItem); } catch (Exception ex) { _Logger.LogWarning( ex, "SignalR push failed for group {Group}", group); } }); var summary = await bll.ProcessMRPRunAsync( trigger, mrpRunId, login, progress, ct).ConfigureAwait(false); await _HubContext.Clients.Group(group) .ReceiveComplete( mrpRunId, summary); } catch (Exception ex) { _Logger.LogError( ex, "MRP run {MrpRunId} failed in processing service", mrpRunId); await _HubContext.Clients.Group(group) .ReceiveError( $"MRP run failed: {ex.Message}"); } } // Entry point for combined MPS → MRP sequential run public async Task ProcessCombinedAsync(MRPRunTriggerDTO trigger,LoginDTO loginDTO, CancellationToken ct = default) { try { // Run MPS (Type=0) first var mpsTrigger = CloneWith(trigger, 0); await ProcessAsync(mpsTrigger, loginDTO, ct).ConfigureAwait(false); // Then run MRP (Type=1) var mrpTrigger = CloneWith(trigger, 1); mrpTrigger.IsCombined = false; // already split await ProcessAsync(mrpTrigger, loginDTO, ct).ConfigureAwait(false); } catch (Exception ex) { _Logger.LogError( ex, "Combined MPS/MRP run failed for MrpId {MrpId}", trigger.MrpId); throw; } } private static MRPRunTriggerDTO CloneWith(MRPRunTriggerDTO src, int typeofRun) { try { return new() { MrpId = src.MrpId, FromDate = src.FromDate, ToDate = src.ToDate, TypeofRun = typeofRun, IncludeException = src.IncludeException, IncludeSafetyStock = src.IncludeSafetyStock, IsPeggingRequired = src.IsPeggingRequired, IsStockPostRequired = src.IsStockPostRequired, IsCombined = src.IsCombined, WorkOUId = src.WorkOUId, TenantId = src.TenantId, UserId = src.UserId, RunScope = src.RunScope, AllocationId = src.AllocationId, SalesOrderId = src.SalesOrderId, FGItemIds = src.FGItemIds, MaxPriorityToInclude = src.MaxPriorityToInclude }; } catch (Exception ex) { throw new InvalidOperationException( $"Failed to clone MRP trigger for TypeofRun {typeofRun}.", ex); } } } }