using EAIAdminDAL.DTO; using EAIAdminDAL.Query; using GB5Shared.DTO.Framework.Login; using GB5Shared.GenerateAutoNumber; using GB5Shared.QueryExecutor; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using static GB5Shared.GB5Constant.Constant; namespace EAIAdminDAL.CustomCode { // NOTE: Global/ClientAttribute-scoped rows (TenantId = -1) are platform-wide — any tenant's // admin can currently read AND write them via this DAL (the WHERE clauses deliberately allow // TenantId = -1 access from every caller). Same "single-tenant-writes-global-config" risk // already flagged for MEAIAGENTROUTE in Part 1 — mitigate the same way: gate the Angular // screens for Global/ClientAttribute-level settings to a SuperAdmin-only menu grant. Not a // DAL-level concern to fix here. public class EAIContextSettingDAL : IEAIContextSettingDAL { private readonly IQueryExecutor _qe; private readonly AutoNumber _autoNumber; private readonly ILogger _logger; public EAIContextSettingDAL(IQueryExecutor qe, AutoNumber autoNumber, ILogger logger) { _qe = qe ?? throw new ArgumentNullException(nameof(qe)); _autoNumber = autoNumber ?? throw new ArgumentNullException(nameof(autoNumber)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } public async Task> GetSettingListAsync( int contextKeyDefinitionId, LoginDTO login, CancellationToken ct = default) { var result = await _qe.QueryAsync( login, EAIContextSettingQB.GET_SETTING_LIST, new { TenantId = login.ClientId, ContextKeyDefinitionId = contextKeyDefinitionId }, cancellationToken: ct).ConfigureAwait(false); return result ?? []; } public async Task GetSettingAsync( int contextSettingId, LoginDTO login, CancellationToken ct = default) { return await _qe.QuerySingleAsync( login, EAIContextSettingQB.GET_SETTING, new { ContextSettingId = contextSettingId, TenantId = login.ClientId }, cancellationToken: ct).ConfigureAwait(false); } public async Task InsertSettingAsync( EAIContextSettingDTO dto, int ownerTenantId, LoginDTO login, CancellationToken ct = default) { var auto = await _autoNumber.GetNumberAsync(1, AUTONUMBERCONSTANT.EAICONTEXTSETTING, login) .ConfigureAwait(false); var newId = auto.StartNumber; var now = DateTime.UtcNow; await _qe.ExecuteAsync( login, EAIContextSettingQB.INSERT_SETTING, new { ContextSettingId = newId, dto.ContextKeyDefinitionId, dto.ScopeLevel, dto.ScopeValue, dto.ContextValue, dto.Priority, TenantId = ownerTenantId, dto.Version, CreatedById = login.UserId, CreatedOn = now }, cancellationToken: ct).ConfigureAwait(false); _logger.LogInformation("DAL | InsertSetting | Completed | NewId={NewId}", newId); return newId; } public async Task UpdateSettingAsync( EAIContextSettingDTO dto, LoginDTO login, CancellationToken ct = default) { await _qe.ExecuteAsync( login, EAIContextSettingQB.UPDATE_SETTING, new { dto.ContextSettingId, TenantId = login.ClientId, dto.ScopeValue, dto.ContextValue, dto.Priority, dto.Version, ModifiedById = login.UserId, ModifiedOn = DateTime.UtcNow }, cancellationToken: ct).ConfigureAwait(false); } public async Task DeleteSettingAsync( int contextSettingId, LoginDTO login, CancellationToken ct = default) { await _qe.ExecuteAsync( login, EAIContextSettingQB.DELETE_SETTING, new { ContextSettingId = contextSettingId, TenantId = login.ClientId, ModifiedById = login.UserId, ModifiedOn = DateTime.UtcNow }, cancellationToken: ct).ConfigureAwait(false); } public async Task> GetCandidateSettingsForResolutionAsync( int tenantId, int roleId, int workOUId, int userId, IReadOnlyList classificationValues, LoginDTO login, CancellationToken ct = default) { // Dapper's `IN @List` needs a non-empty list — a placeholder that can never match a // real "Dimension:Value" string (those always contain a colon between two codes). var classificationList = classificationValues.Count > 0 ? classificationValues.ToList() : new List { "__NONE__" }; var result = await _qe.QueryAsync( login, EAIContextSettingQB.GET_CANDIDATE_SETTINGS_FOR_RESOLUTION, new { TenantId = tenantId, TenantIdStr = tenantId.ToString(), RoleIdStr = roleId.ToString(), WorkOUIdStr = workOUId.ToString(), UserIdStr = userId.ToString(), ClassificationValues = classificationList }, cancellationToken: ct).ConfigureAwait(false); return result ?? []; } public async Task> GetClassificationValuesForTenantAsync( int tenantId, LoginDTO login, CancellationToken ct = default) { var result = await _qe.QueryAsync( login, EAIContextSettingQB.GET_CLASSIFICATION_VALUES_FOR_TENANT, new { TenantId = tenantId }, cancellationToken: ct).ConfigureAwait(false); return result ?? []; } public async Task GetUserRoleAndOuAsync( int userId, int tenantId, LoginDTO login, CancellationToken ct = default) { return await _qe.QuerySingleAsync( login, EAIContextSettingQB.GET_USER_ROLE_AND_OU, new { UserId = userId, TenantId = tenantId }, cancellationToken: ct).ConfigureAwait(false); } } }