using AMREZ.EOP.Abstractions.Infrastructures.Repositories; using AMREZ.EOP.Abstractions.Storage; using AMREZ.EOP.Domain.Entities.Tenancy; using AMREZ.EOP.Infrastructures.Data; using AMREZ.EOP.Infrastructures.Options; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Options; namespace AMREZ.EOP.Infrastructures.Repositories; public sealed class TenantRepository : ITenantRepository { private readonly IDbScope _scope; public TenantRepository(IDbScope scope) => _scope = scope; private AppDbContext Db() => _scope.Get(); private static string Norm(string s) => (s ?? string.Empty).Trim().ToLowerInvariant(); public Task TenantExistsAsync(string tenantKey, CancellationToken ct = default) { var key = Norm(tenantKey); return Db().Set().AsNoTracking().AnyAsync(x => x.TenantKey == key, ct); } public Task GetAsync(string tenantKey, CancellationToken ct = default) { var key = Norm(tenantKey); return Db().Set().AsNoTracking().FirstOrDefaultAsync(x => x.TenantKey == key, ct); } public async Task> ListTenantsAsync(CancellationToken ct = default) => await Db().Set().AsNoTracking().OrderBy(x => x.TenantKey).ToListAsync(ct); public async Task CreateAsync(TenantConfig row, CancellationToken ct = default) { row.TenantKey = Norm(row.TenantKey); row.Schema = string.IsNullOrWhiteSpace(row.Schema) ? null : row.Schema!.Trim(); row.ConnectionString = string.IsNullOrWhiteSpace(row.ConnectionString) ? null : row.ConnectionString!.Trim(); row.UpdatedAtUtc = DateTimeOffset.UtcNow; await Db().Set().AddAsync(row, ct); return await Db().SaveChangesAsync(ct) > 0; } public async Task UpdateAsync(TenantConfig row, DateTimeOffset? ifUnmodifiedSince, CancellationToken ct = default) { var key = Norm(row.TenantKey); var db = Db(); var cur = await db.Set().FirstOrDefaultAsync(x => x.TenantKey == key, ct); if (cur is null) return false; if (ifUnmodifiedSince.HasValue && cur.UpdatedAtUtc > ifUnmodifiedSince.Value) return false; // 412 cur.Schema = row.Schema is null ? cur.Schema : (string.IsNullOrWhiteSpace(row.Schema) ? null : row.Schema.Trim()); cur.ConnectionString = row.ConnectionString is null ? cur.ConnectionString : (string.IsNullOrWhiteSpace(row.ConnectionString) ? null : row.ConnectionString.Trim()); cur.Mode = row.Mode; cur.IsActive = row.IsActive; cur.UpdatedAtUtc = DateTimeOffset.UtcNow; return await db.SaveChangesAsync(ct) > 0; } public async Task DeleteAsync(string tenantKey, CancellationToken ct = default) { var key = Norm(tenantKey); var db = Db(); var t = await db.Set().FirstOrDefaultAsync(x => x.TenantKey == key, ct); if (t is null) return false; // ❌ ไม่ลบนะ — ✅ deactivate if (t.IsActive) { t.IsActive = false; t.UpdatedAtUtc = DateTimeOffset.UtcNow; } var domains = await db.Set() .Where(d => d.TenantKey == key && d.IsActive) .ToListAsync(ct); foreach (var d in domains) { d.IsActive = false; d.UpdatedAtUtc = DateTimeOffset.UtcNow; } return await db.SaveChangesAsync(ct) > 0; } public async Task MapDomainAsync(string domain, string tenantKey, CancellationToken ct = default) { var d = Norm(domain); var key = Norm(tenantKey); if (!await Db().Set().AnyAsync(x => x.TenantKey == key && x.IsActive, ct)) return false; var db = Db(); var ex = await db.Set().FirstOrDefaultAsync(x => x.Domain == d, ct); if (ex is null) await db.Set().AddAsync(new TenantDomain { Domain = d, TenantKey = key, IsPlatformBaseDomain = false, IsActive = true, UpdatedAtUtc = DateTimeOffset.UtcNow }, ct); else { ex.TenantKey = key; ex.IsPlatformBaseDomain = false; ex.IsActive = true; ex.UpdatedAtUtc = DateTimeOffset.UtcNow; } return await db.SaveChangesAsync(ct) > 0; } public async Task UnmapDomainAsync(string domain, CancellationToken ct = default) { var d = Norm(domain); var db = Db(); var ex = await db.Set().FirstOrDefaultAsync(x => x.Domain == d, ct); if (ex is null) return false; ex.IsActive = false; ex.TenantKey = null; ex.IsPlatformBaseDomain = false; ex.UpdatedAtUtc = DateTimeOffset.UtcNow; return await db.SaveChangesAsync(ct) > 0; } public async Task> ListDomainsAsync(string? tenantKey, CancellationToken ct = default) { var db = Db(); var q = db.Set().AsNoTracking(); if (!string.IsNullOrWhiteSpace(tenantKey)) { var key = Norm(tenantKey!); q = q.Where(x => x.TenantKey == key || x.IsPlatformBaseDomain); } return await q.OrderBy(x => x.Domain).ToListAsync(ct); } public async Task AddBaseDomainAsync(string baseDomain, CancellationToken ct = default) { var d = Norm(baseDomain); var db = Db(); var ex = await db.Set().FirstOrDefaultAsync(x => x.Domain == d, ct); if (ex is null) await db.Set().AddAsync(new TenantDomain { Domain = d, TenantKey = null, IsPlatformBaseDomain = true, IsActive = true, UpdatedAtUtc = DateTimeOffset.UtcNow }, ct); else { ex.TenantKey = null; ex.IsPlatformBaseDomain = true; ex.IsActive = true; ex.UpdatedAtUtc = DateTimeOffset.UtcNow; } return await db.SaveChangesAsync(ct) > 0; } public async Task RemoveBaseDomainAsync(string baseDomain, CancellationToken ct = default) { var d = Norm(baseDomain); var db = Db(); var ex = await db.Set() .FirstOrDefaultAsync(x => x.Domain == d && x.IsPlatformBaseDomain, ct); if (ex is null) return false; ex.IsActive = false; ex.UpdatedAtUtc = DateTimeOffset.UtcNow; return await db.SaveChangesAsync(ct) > 0; } }