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);
}
}