using CredentialManager.Data; using CredentialManager.Models; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; namespace CredentialManager.Services; /// /// Servizio per la gestione delle associazioni tra record sorgente e destinazione /// public class RecordAssociationService : IRecordAssociationService { private readonly CredentialDbContext _context; private readonly ILogger _logger; public RecordAssociationService( CredentialDbContext context, ILogger logger) { _context = context; _logger = logger; } public async Task SaveAssociationAsync(RecordAssociation association) { try { // Controlla se esiste già un'associazione per questa combinazione var existing = await _context.RecordAssociations .FirstOrDefaultAsync(ra => ra.SourceName == association.SourceName && ra.SourceKey == association.SourceKey && ra.DestinationEntity == association.DestinationEntity && ra.IsActive); if (existing != null) { // Aggiorna l'associazione esistente existing.DestinationId = association.DestinationId; existing.RestCredentialName = association.RestCredentialName; existing.UpdatedAt = DateTime.UtcNow; existing.AdditionalInfo = association.AdditionalInfo; _context.RecordAssociations.Update(existing); await _context.SaveChangesAsync(); _logger.LogInformation("Associazione aggiornata: {SourceName}/{SourceKey} -> {DestinationEntity}/{DestinationId}", association.SourceName, association.SourceKey, association.DestinationEntity, association.DestinationId); return existing.Id; } else { // Crea nuova associazione _context.RecordAssociations.Add(association); await _context.SaveChangesAsync(); _logger.LogInformation("Nuova associazione creata: {SourceName}/{SourceKey} -> {DestinationEntity}/{DestinationId}", association.SourceName, association.SourceKey, association.DestinationEntity, association.DestinationId); return association.Id; } } catch (Exception ex) { _logger.LogError(ex, "Errore nel salvare l'associazione: {SourceName}/{SourceKey} -> {DestinationEntity}", association.SourceName, association.SourceKey, association.DestinationEntity); throw; } } public async Task FindAssociationAsync(string sourceName, string sourceKey, string destinationEntity) { try { return await _context.RecordAssociations .FirstOrDefaultAsync(ra => ra.SourceName == sourceName && ra.SourceKey == sourceKey && ra.DestinationEntity == destinationEntity && ra.IsActive); } catch (Exception ex) { _logger.LogError(ex, "Errore nella ricerca dell'associazione: {SourceName}/{SourceKey} -> {DestinationEntity}", sourceName, sourceKey, destinationEntity); throw; } } public async Task> GetAssociationsBySourceAsync(string sourceName, string sourceType) { try { return await _context.RecordAssociations .Where(ra => ra.SourceName == sourceName && ra.SourceType == sourceType && ra.IsActive) .OrderByDescending(ra => ra.CreatedAt) .ToListAsync(); } catch (Exception ex) { _logger.LogError(ex, "Errore nel recupero delle associazioni per sorgente: {SourceName} ({SourceType})", sourceName, sourceType); throw; } } public async Task> GetAssociationsByDestinationAsync(string destinationEntity, string restCredentialName) { try { return await _context.RecordAssociations .Where(ra => ra.DestinationEntity == destinationEntity && ra.RestCredentialName == restCredentialName && ra.IsActive) .OrderByDescending(ra => ra.CreatedAt) .ToListAsync(); } catch (Exception ex) { _logger.LogError(ex, "Errore nel recupero delle associazioni per destinazione: {DestinationEntity} ({RestCredentialName})", destinationEntity, restCredentialName); throw; } } public async Task> GetAllActiveAssociationsAsync() { try { return await _context.RecordAssociations .Where(ra => ra.IsActive) .OrderByDescending(ra => ra.CreatedAt) .ToListAsync(); } catch (Exception ex) { _logger.LogError(ex, "Errore nel recupero di tutte le associazioni attive"); throw; } } public async Task UpdateAssociationAsync(RecordAssociation association) { try { var existing = await _context.RecordAssociations.FindAsync(association.Id); if (existing == null) { _logger.LogWarning("Associazione con ID {Id} non trovata per l'aggiornamento", association.Id); return false; } existing.DestinationId = association.DestinationId; existing.RestCredentialName = association.RestCredentialName; existing.UpdatedAt = DateTime.UtcNow; existing.AdditionalInfo = association.AdditionalInfo; existing.IsActive = association.IsActive; _context.RecordAssociations.Update(existing); await _context.SaveChangesAsync(); _logger.LogInformation("Associazione aggiornata: ID {Id}", association.Id); return true; } catch (Exception ex) { _logger.LogError(ex, "Errore nell'aggiornamento dell'associazione: ID {Id}", association.Id); throw; } } public async Task DeactivateAssociationAsync(int id) { try { var association = await _context.RecordAssociations.FindAsync(id); if (association == null) { _logger.LogWarning("Associazione con ID {Id} non trovata per la disattivazione", id); return false; } association.IsActive = false; association.UpdatedAt = DateTime.UtcNow; _context.RecordAssociations.Update(association); await _context.SaveChangesAsync(); _logger.LogInformation("Associazione disattivata: ID {Id}", id); return true; } catch (Exception ex) { _logger.LogError(ex, "Errore nella disattivazione dell'associazione: ID {Id}", id); throw; } } public async Task DeleteAssociationAsync(int id) { try { var association = await _context.RecordAssociations.FindAsync(id); if (association == null) { _logger.LogWarning("Associazione con ID {Id} non trovata per l'eliminazione", id); return false; } _context.RecordAssociations.Remove(association); await _context.SaveChangesAsync(); _logger.LogInformation("Associazione eliminata: ID {Id}", id); return true; } catch (Exception ex) { _logger.LogError(ex, "Errore nell'eliminazione dell'associazione: ID {Id}", id); throw; } } public async Task CleanupOldAssociationsAsync(TimeSpan olderThan) { try { var cutoffDate = DateTime.UtcNow - olderThan; var oldAssociations = await _context.RecordAssociations .Where(ra => ra.CreatedAt < cutoffDate && !ra.IsActive) .ToListAsync(); if (oldAssociations.Any()) { _context.RecordAssociations.RemoveRange(oldAssociations); await _context.SaveChangesAsync(); _logger.LogInformation("Pulite {Count} associazioni obsolete più vecchie di {Cutoff}", oldAssociations.Count, cutoffDate); } return oldAssociations.Count; } catch (Exception ex) { _logger.LogError(ex, "Errore nella pulizia delle associazioni obsolete"); throw; } } }