using FrameworkDAL.CustomCode.DirectAction.DirectActionAdmin; using FrameworkDAL.DTO.DirectAction; 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.DirectAction.DirectActionAdmin { public class DirectActionAdminBLL : IDirectActionAdminBLL { private static readonly Regex DirectActionCodePattern = new(@"^[A-Za-z0-9_]{1,50}$", RegexOptions.Compiled); private readonly IDirectActionAdminDAL _dal; private readonly ILogger _logger; public DirectActionAdminBLL(IDirectActionAdminDAL dal, ILogger logger) { _dal = dal ?? throw new ArgumentNullException(nameof(dal)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } public async Task> GetDirectActionListAsync( string? search, LoginDTO login, CancellationToken ct = default) { try { _logger.LogInformation("BLL | GetDirectActionList | Tenant={TenantId} | Search={Search}", login.ClientId, search); return await _dal.GetDirectActionListAsync(search, login, ct).ConfigureAwait(false); } catch (Exception ex) { _logger.LogError(ex, "BLL | GetDirectActionList | Error | Tenant={TenantId}", login.ClientId); throw; } } public async Task GetDirectActionAsync( int directActionId, LoginDTO login, CancellationToken ct = default) { try { _logger.LogInformation("BLL | GetDirectAction | Tenant={TenantId} | Id={Id}", login.ClientId, directActionId); return await _dal.GetDirectActionAsync(directActionId, login, ct).ConfigureAwait(false); } catch (Exception ex) { _logger.LogError(ex, "BLL | GetDirectAction | Error | Id={Id}", directActionId); throw; } } public async Task SaveDirectActionAsync( DirectActionAdminDTO dto, LoginDTO login, CancellationToken ct = default) { try { _logger.LogInformation( "BLL | SaveDirectAction | Validate | Tenant={TenantId} | Code={Code}", login.ClientId, dto.DirectActionCode); ValidateDirectActionCode(dto.DirectActionCode); if (string.IsNullOrWhiteSpace(dto.DirectActionName)) throw new ArgumentException("DirectActionName is required."); if (string.IsNullOrWhiteSpace(dto.ContextIdField)) throw new ArgumentException("ContextIdField is required."); if (string.IsNullOrWhiteSpace(dto.AssigneeUserIdField)) throw new ArgumentException("AssigneeUserIdField is required."); // DIRECTACTIONCODE is unique per (CODE, TENANTID) — UK_MDIRECTACTION_CODE_TENANTID. bool codeExists = await _dal.DirectActionCodeExistsAsync( dto.DirectActionCode, dto.DirectActionId, login, ct).ConfigureAwait(false); if (codeExists) throw new InvalidOperationException( $"DirectActionCode '{dto.DirectActionCode}' already exists."); dto.TenantId = login.ClientId; var isNew = dto.DirectActionId == 0; _logger.LogInformation( "BLL | SaveDirectAction | Persist | Tenant={TenantId} | Code={Code} | IsNew={IsNew}", login.ClientId, dto.DirectActionCode, isNew); if (isNew) { var newId = await _dal.InsertDirectActionAsync(dto, login, ct).ConfigureAwait(false); _logger.LogInformation("BLL | SaveDirectAction | Inserted | NewId={NewId}", newId); return $"{SuccessResponse.SaveSuccessMessage} {newId}"; } else { await _dal.UpdateDirectActionAsync(dto, login, ct).ConfigureAwait(false); _logger.LogInformation("BLL | SaveDirectAction | Updated | Id={Id}", dto.DirectActionId); return SuccessResponse.UpdateSuccess; } } catch (Exception ex) { _logger.LogError(ex, "BLL | SaveDirectAction | Error | Tenant={TenantId} | Code={Code}", login.ClientId, dto.DirectActionCode); throw; } } public async Task DeleteDirectActionAsync( int directActionId, LoginDTO login, CancellationToken ct = default) { try { _logger.LogInformation("BLL | DeleteDirectAction | Tenant={TenantId} | Id={Id}", login.ClientId, directActionId); // AutoNumber-generated ids in this repo are negative — only 0 (C# default, // "never set") is genuinely invalid. if (directActionId == 0) throw new ArgumentException("DirectActionId must reference a valid record."); await _dal.DeleteDirectActionAsync(directActionId, login, ct).ConfigureAwait(false); _logger.LogInformation("BLL | DeleteDirectAction | Completed | Id={Id}", directActionId); return SuccessResponse.DeleteSuccessMessage; } catch (Exception ex) { _logger.LogError(ex, "BLL | DeleteDirectAction | Error | Id={Id}", directActionId); throw; } } private static void ValidateDirectActionCode(string directActionCode) { if (string.IsNullOrWhiteSpace(directActionCode)) throw new ArgumentException("DirectActionCode is required."); if (!DirectActionCodePattern.IsMatch(directActionCode)) throw new ArgumentException( "DirectActionCode must contain only alphanumeric characters and underscores (max 50 chars)."); } } }