using AMREZ.EOP.Abstractions.Applications.Tenancy; using AMREZ.EOP.Abstractions.Infrastructures.Repositories; using AMREZ.EOP.Abstractions.Storage; using AMREZ.EOP.Contracts.DTOs.Common; using AMREZ.EOP.Contracts.DTOs.MasterData.ComplianceStatus; using AMREZ.EOP.Domain.Entities.MasterData; using AMREZ.EOP.Infrastructures.Data; using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; namespace AMREZ.EOP.Infrastructures.Repositories; public sealed class ComplianceStatusRepository : IComplianceStatusRepository { private readonly IDbScope _scope; private readonly ITenantResolver _tenantResolver; private readonly IHttpContextAccessor _http; public ComplianceStatusRepository(IDbScope scope, ITenantResolver tenantResolver, IHttpContextAccessor http) { _scope = scope; _tenantResolver = tenantResolver; _http = http; } private Guid EnsureTenant(out AppDbContext db) { var http = _http.HttpContext ?? throw new InvalidOperationException("No HttpContext"); var tc = _tenantResolver.Resolve(http) ?? throw new InvalidOperationException("No tenant"); _scope.EnsureForTenant(tc); db = _scope.Get(); if (!string.IsNullOrWhiteSpace(tc.Id) && Guid.TryParse(tc.Id, out var g) && g != Guid.Empty) return g; var key = (http.Items.TryGetValue("TargetTenantKey", out var v) ? v as string : null) ?? tc.Id; if (string.IsNullOrWhiteSpace(key)) throw new InvalidOperationException("Tenant key missing"); var cfg = db.Set() .AsNoTracking() .FirstOrDefault(x => x.TenantKey == key); if (cfg is null) throw new InvalidOperationException($"Tenant '{key}' not found"); return cfg.TenantId; } public async Task GetAsync(Guid id, CancellationToken ct = default) { var tid = EnsureTenant(out var db); return await db.ComplianceStatuses.AsNoTracking() .Where(x => x.Id == id && !x.IsDeleted) .Where(x => x.Scope == "tenant" ? x.TenantId == tid : true) .FirstOrDefaultAsync(ct); } public async Task> SearchEffectiveAsync(Guid tenantId, ComplianceStatusListRequest req, CancellationToken ct = default) { var tid = EnsureTenant(out var db); var tenantQ = db.ComplianceStatuses.AsNoTracking() .Where(b => b.Scope == "tenant" && b.TenantId == tid && !b.IsDeleted); var overriddenIds = db.ComplianceStatuses .Where(t => t.Scope == "tenant" && t.TenantId == tid && !t.IsDeleted && t.OverridesGlobalId != null) .Select(t => t.OverridesGlobalId!.Value); var blockedIds = db.ComplianceStatusBlocks .Where(b => b.TenantId == tid) .Select(b => b.GlobalId); var globalsQ = db.ComplianceStatuses.AsNoTracking() .Where(g => g.Scope == "global" && !g.IsDeleted) .Where(g => !overriddenIds.Contains(g.Id) && !blockedIds.Contains(g.Id)); var q = tenantQ.Union(globalsQ); if (!req.IncludeInactive) q = q.Where(x => x.IsActive); if (!string.IsNullOrWhiteSpace(req.Search)) { var s = req.Search.Trim(); q = q.Where(x => x.Code.Contains(s) || x.Name.Contains(s)); } var total = await q.CountAsync(ct); var items = await q .OrderBy(x => x.Code) .ThenBy(x => x.Name) .Skip((req.Page - 1) * req.PageSize) .Take(req.PageSize) .ToListAsync(ct); return new PagedResponse(req.Page, req.PageSize, total, items); } public async Task CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default) { var tid = EnsureTenant(out var db); var norm = code.Trim(); return await db.ComplianceStatuses.AnyAsync(b => !b.IsDeleted && b.Code == norm && (b.Scope == "tenant" && b.TenantId == tid), ct); } public async Task AddAsync(ComplianceStatus entity, CancellationToken ct = default) { EnsureTenant(out var db); await db.ComplianceStatuses.AddAsync(entity, ct); await db.SaveChangesAsync(ct); } public async Task UpdateAsync(ComplianceStatus entity, CancellationToken ct = default) { EnsureTenant(out var db); db.ComplianceStatuses.Update(entity); await db.SaveChangesAsync(ct); } public async Task SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default) { var tid = EnsureTenant(out var db); var b = await db.ComplianceStatuses.FirstOrDefaultAsync(x => x.Id == id && !x.IsDeleted, ct); if (b is null) return 0; if (b.Scope == "global") { var exists = await db.ComplianceStatusBlocks .AnyAsync(x => x.TenantId == tid && x.GlobalId == id, ct); if (exists) return 0; var block = new ComplianceStatusBlock { Id = Guid.NewGuid(), TenantId = tid, GlobalId = id }; await db.ComplianceStatusBlocks.AddAsync(block, ct); return await db.SaveChangesAsync(ct); } if (b.TenantId != tid) return 0; b.IsDeleted = true; return await db.SaveChangesAsync(ct); } }