using AmpDAL.CustomCode.ApiRequestChecklist; using AmpDAL.DTO.ApiRequestChecklist; using GB5Shared.DTO.Framework.Login; using GB5Shared.GenerateAutoNumber; using GB5Shared.Resource.Response; using Microsoft.Extensions.Logging; namespace AMPBLL.ApiRequestChecklist; public class ApiRequestChecklistBLL( IApiRequestChecklistDAL _dal, AutoNumber _autoNumber, ILogger _logger ) : IApiRequestChecklistBLL { // Stages 0,1,3,10,11,12 are system-managed — no checklist seeding required. // Only stages 2,4,5,6,7,8,9 have sign-off items. private static readonly IReadOnlyDictionary> StageItems = new Dictionary> { [2] = ["Duplicate review complete", "Business justification approved", "Domain and type confirmed"], [4] = ["MAPI draft record created (LINKEDAPIID set)", "API domain confirmed", "Proposed version number agreed"], [5] = ["MAPIINTERFACE record created", "HTTP method and path agreed", "Spec type selected (OpenAPI/AsyncAPI)"], [6] = ["MAPIINTERFACE parsed successfully", "Error codes standardized", "Pagination strategy confirmed", "Auth method documented"], [7] = ["MAPISCHEMA request/response JSON populated", "All MAPIPARAMETER ISREQUIRED set", "Auth tested", "Pagination tested", "Error responses tested"], [8] = ["Consumer tested against spec", "SLA response-time benchmarks met", "No undocumented breaking changes"], [9] = ["MAPI ABOUTAPI non-empty", "MAPI KEYFEATURES non-empty", "MAPI USECASES non-empty", "MAPIARTIFACT documentation uploaded", "MAPIEXPOSUREPOLICY record exists", "Consumers notified"] }; public async Task> GetChecklistAsync(int apiRequestId, byte? stage, LoginDTO login, CancellationToken ct) { if (apiRequestId == 0) throw new ArgumentException("ApiRequestId must be a positive number."); try { return stage.HasValue ? await _dal.GetByStageAsync(apiRequestId, stage.Value, login.ClientId, login, ct).ConfigureAwait(false) : await _dal.GetByRequestAsync(apiRequestId, login.ClientId, login, ct).ConfigureAwait(false); } catch (Exception ex) { _logger.LogError(ex, "GetChecklist failed for ApiRequestId {ApiRequestId}", apiRequestId); throw; } } public async Task CompleteItemAsync(int checklistId, int apiRequestId, LoginDTO login, CancellationToken ct) { if (checklistId == 0) throw new ArgumentException("ChecklistId must be a positive number."); if (apiRequestId == 0) throw new ArgumentException("ApiRequestId must be a positive number."); try { await _dal.CompleteItemAsync(checklistId, apiRequestId, login.UserId, login.ClientId, login, ct).ConfigureAwait(false); return SuccessResponse.UpdateSuccess; } catch (Exception ex) { _logger.LogError(ex, "CompleteItem failed for ChecklistId {ChecklistId}", checklistId); throw; } } public async Task SeedStageItemsAsync(int apiRequestId, byte stage, LoginDTO login, CancellationToken ct) { if (apiRequestId == 0) throw new ArgumentException("ApiRequestId must be a positive number."); if (!StageItems.TryGetValue(stage, out var items)) throw new ArgumentException($"Stage {stage} is system-managed; no checklist required."); // Guard: skip if already seeded for this request + stage var existing = await _dal.GetByStageAsync(apiRequestId, stage, login.ClientId, login, ct).ConfigureAwait(false); if (existing.Count > 0) { _logger.LogWarning( "SeedStageItems skipped — already seeded. ApiRequestId {ApiRequestId} | Stage {Stage}", apiRequestId, stage); return 0; } try { var autoNumberDto = await _autoNumber.GetNumberAsync(items.Count, "MAPIREQUESTCHECKLIST", login).ConfigureAwait(false); if (autoNumberDto is null || autoNumberDto.StartNumber <= 0) throw new InvalidOperationException($"AutoNumber returned invalid result for Stage {stage}."); int nextId = autoNumberDto.StartNumber; for (short i = 0; i < items.Count; i++) { ct.ThrowIfCancellationRequested(); var dto = new ApiRequestChecklistSaveDTO { ChecklistId = nextId++, TenantId = login.ClientId, ApiRequestId = apiRequestId, Stage = stage, ItemText = items[i], IsComplete = 0, SortOrder = (short)(i + 1) }; await _dal.InsertChecklistItemAsync(dto, login, ct).ConfigureAwait(false); } _logger.LogInformation( "SeedStageItems completed. ApiRequestId {ApiRequestId} | Stage {Stage} | Items seeded {Count}", apiRequestId, stage, items.Count); return items.Count; } catch (Exception ex) when (ex is not ArgumentException && ex is not InvalidOperationException) { _logger.LogError(ex, "SeedStageItems failed. ApiRequestId {ApiRequestId} | Stage {Stage}", apiRequestId, stage); throw; } } }