using FrameworkDAL.CustomCode.EIPConversation.EIPActionRegistryAdmin; using FrameworkDAL.DTO.EIPConversation; using GB5Shared.DTO.Framework.Login; using GB5Shared.Resource.Response; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; namespace FrameworkBLL.EIPConversation.EIPAdmin.EIPActionRegistryAdmin { public class EIPActionRegistryAdminBLL : IEIPActionRegistryAdminBLL { private static readonly Regex ActionCodePattern = new(@"^[A-Za-z0-9_.]{1,200}$", RegexOptions.Compiled); private readonly IEIPActionRegistryAdminDAL _dal; private readonly ILogger _logger; public EIPActionRegistryAdminBLL(IEIPActionRegistryAdminDAL dal, ILogger logger) { _dal = dal ?? throw new ArgumentNullException(nameof(dal)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } public async Task> GetActionRegistryListAsync( string? search, LoginDTO login, CancellationToken ct = default) { try { _logger.LogInformation("BLL | GetActionRegistryList | Tenant={TenantId} | Search={Search}", login.ClientId, search); return await _dal.GetActionRegistryListAsync(search, login, ct).ConfigureAwait(false); } catch (Exception ex) { _logger.LogError(ex, "BLL | GetActionRegistryList | Error | Tenant={TenantId}", login.ClientId); throw; } } public async Task GetActionRegistryAsync( int actionId, LoginDTO login, CancellationToken ct = default) { try { _logger.LogInformation("BLL | GetActionRegistry | Tenant={TenantId} | Id={Id}", login.ClientId, actionId); return await _dal.GetActionRegistryAsync(actionId, login, ct).ConfigureAwait(false); } catch (Exception ex) { _logger.LogError(ex, "BLL | GetActionRegistry | Error | Id={Id}", actionId); throw; } } public async Task SaveActionRegistryAsync( EIPActionRegistryAdminDTO dto, LoginDTO login, CancellationToken ct = default) { try { _logger.LogInformation( "BLL | SaveActionRegistry | Validate | Tenant={TenantId} | Code={Code}", login.ClientId, dto.ActionCode); ValidateActionCode(dto.ActionCode); if (string.IsNullOrWhiteSpace(dto.ActionName)) throw new ArgumentException("ActionName is required."); if (dto.IsIdempotent != 0 && dto.IsIdempotent != 1) throw new ArgumentException("IsIdempotent must be 0 or 1."); if (dto.IsActive != 0 && dto.IsActive != 1) throw new ArgumentException("IsActive must be 0 or 1."); if (dto.TimeoutMs <= 0) throw new ArgumentException("TimeoutMs must be greater than 0."); // ACTIONCODE is globally unique (UK_MEIPACTIONREGISTRY_ACTIONCODE) — not per-tenant. bool codeExists = await _dal.ActionCodeExistsAsync( dto.ActionCode, dto.ActionId, login, ct).ConfigureAwait(false); if (codeExists) throw new InvalidOperationException($"ActionCode '{dto.ActionCode}' already exists."); dto.TenantId = login.ClientId; var isNew = dto.ActionId == 0; _logger.LogInformation( "BLL | SaveActionRegistry | Persist | Tenant={TenantId} | Code={Code} | IsNew={IsNew}", login.ClientId, dto.ActionCode, isNew); if (isNew) { var newId = await _dal.InsertActionRegistryAsync(dto, login, ct).ConfigureAwait(false); _logger.LogInformation("BLL | SaveActionRegistry | Inserted | NewId={NewId}", newId); return $"{SuccessResponse.SaveSuccessMessage} {newId}"; } else { await _dal.UpdateActionRegistryAsync(dto, login, ct).ConfigureAwait(false); _logger.LogInformation("BLL | SaveActionRegistry | Updated | Id={Id}", dto.ActionId); return SuccessResponse.UpdateSuccess; } } catch (Exception ex) { _logger.LogError(ex, "BLL | SaveActionRegistry | Error | Tenant={TenantId} | Code={Code}", login.ClientId, dto.ActionCode); throw; } } public async Task DeleteActionRegistryAsync( int actionId, LoginDTO login, CancellationToken ct = default) { try { _logger.LogInformation("BLL | DeleteActionRegistry | Tenant={TenantId} | Id={Id}", login.ClientId, actionId); if (actionId == 0) throw new ArgumentException("ActionId must reference a valid record."); await _dal.DeleteActionRegistryAsync(actionId, login, ct).ConfigureAwait(false); _logger.LogInformation("BLL | DeleteActionRegistry | Completed | Id={Id}", actionId); return SuccessResponse.DeleteSuccessMessage; } catch (Exception ex) { _logger.LogError(ex, "BLL | DeleteActionRegistry | Error | Id={Id}", actionId); throw; } } private static void ValidateActionCode(string actionCode) { if (string.IsNullOrWhiteSpace(actionCode)) throw new ArgumentException("ActionCode is required."); if (!ActionCodePattern.IsMatch(actionCode)) throw new ArgumentException( "ActionCode must contain only alphanumeric characters, underscores and dots (max 200 chars)."); } } }