using ScadaLink.Commons.Entities.Templates; using ScadaLink.Commons.Interfaces.Repositories; using ScadaLink.Commons.Types; namespace ScadaLink.TemplateEngine.Services; /// /// Enforces template deletion constraints (WP-25). /// Template deletion is blocked when: /// - Instances reference the template /// - Child templates reference it (as parent) /// - Other templates compose it /// Returns clear error messages listing the referencing entities. /// public class TemplateDeletionService { private readonly ITemplateEngineRepository _repository; public TemplateDeletionService(ITemplateEngineRepository repository) { _repository = repository ?? throw new ArgumentNullException(nameof(repository)); } /// /// Checks whether a template can be safely deleted and returns any blocking reasons. /// public async Task> CanDeleteTemplateAsync(int templateId, CancellationToken cancellationToken = default) { var template = await _repository.GetTemplateByIdAsync(templateId, cancellationToken); if (template == null) return Result.Failure($"Template with ID {templateId} not found."); var errors = new List(); // Check 1: Instances reference this template var instances = await _repository.GetInstancesByTemplateIdAsync(templateId, cancellationToken); if (instances.Count > 0) { var names = string.Join(", ", instances.Select(i => i.UniqueName).Take(10)); errors.Add($"Cannot delete template '{template.Name}': {instances.Count} instance(s) reference it ({names}{(instances.Count > 10 ? "..." : "")})."); } // Check 2: Child templates reference it as parent var allTemplates = await _repository.GetAllTemplatesAsync(cancellationToken); var childTemplates = allTemplates.Where(t => t.ParentTemplateId == templateId).ToList(); if (childTemplates.Count > 0) { var names = string.Join(", ", childTemplates.Select(t => t.Name).Take(10)); errors.Add($"Cannot delete template '{template.Name}': {childTemplates.Count} child template(s) inherit from it ({names}{(childTemplates.Count > 10 ? "..." : "")})."); } // Check 3: Other templates compose it var composingTemplates = new List<(string TemplateName, string InstanceName)>(); foreach (var t in allTemplates) { var compositions = await _repository.GetCompositionsByTemplateIdAsync(t.Id, cancellationToken); foreach (var comp in compositions) { if (comp.ComposedTemplateId == templateId) composingTemplates.Add((t.Name, comp.InstanceName)); } } if (composingTemplates.Count > 0) { var details = string.Join(", ", composingTemplates.Take(10).Select(c => $"'{c.TemplateName}' (as '{c.InstanceName}')")); errors.Add($"Cannot delete template '{template.Name}': {composingTemplates.Count} template(s) compose it ({details}{(composingTemplates.Count > 10 ? "..." : "")})."); } if (errors.Count > 0) return Result.Failure(string.Join(" ", errors)); return Result.Success(true); } /// /// Deletes a template after checking all constraints. /// public async Task> DeleteTemplateAsync(int templateId, CancellationToken cancellationToken = default) { var canDelete = await CanDeleteTemplateAsync(templateId, cancellationToken); if (canDelete.IsFailure) return canDelete; await _repository.DeleteTemplateAsync(templateId, cancellationToken); await _repository.SaveChangesAsync(cancellationToken); return Result.Success(true); } }