using ScadaLink.Commons.Entities.Templates;
using ScadaLink.Commons.Interfaces.Repositories;
using ScadaLink.Commons.Types;

namespace ScadaLink.TemplateEngine.Services;

/// <summary>
/// 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.
/// </summary>
public class TemplateDeletionService
{
    private readonly ITemplateEngineRepository _repository;

    public TemplateDeletionService(ITemplateEngineRepository repository)
    {
        _repository = repository ?? throw new ArgumentNullException(nameof(repository));
    }

    /// <summary>
    /// Checks whether a template can be safely deleted and returns any blocking reasons.
    /// </summary>
    public async Task<Result<bool>> CanDeleteTemplateAsync(int templateId, CancellationToken cancellationToken = default)
    {
        var template = await _repository.GetTemplateByIdAsync(templateId, cancellationToken);
        if (template == null)
            return Result<bool>.Failure($"Template with ID {templateId} not found.");

        if (template.IsDerived)
            return Result<bool>.Failure(
                $"Cannot delete template '{template.Name}': it is a derived template. " +
                "Remove the owning composition on its parent template instead.");

        var errors = new List<string>();

        // 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. Split derived vs. regular.
        var allTemplates = await _repository.GetAllTemplatesAsync(cancellationToken);
        var inheritors = allTemplates.Where(t => t.ParentTemplateId == templateId).ToList();
        var regularChildren = inheritors.Where(t => !t.IsDerived).ToList();
        var derivatives = inheritors.Where(t => t.IsDerived).ToList();

        if (regularChildren.Count > 0)
        {
            var names = string.Join(", ", regularChildren.Select(t => t.Name).Take(10));
            errors.Add($"Cannot delete template '{template.Name}': {regularChildren.Count} child template(s) inherit from it ({names}{(regularChildren.Count > 10 ? "..." : "")}).");
        }

        if (derivatives.Count > 0)
        {
            var compIds = derivatives.Select(d => d.OwnerCompositionId).Where(id => id.HasValue).Select(id => id!.Value).ToHashSet();
            var ownerLookup = allTemplates
                .SelectMany(t => t.Compositions.Select(c => new { Owner = t, Composition = c }))
                .Where(x => compIds.Contains(x.Composition.Id))
                .ToDictionary(x => x.Composition.Id, x => $"'{x.Owner.Name}' (as '{x.Composition.InstanceName}')");
            var details = string.Join(", ", derivatives.Take(10).Select(d =>
                d.OwnerCompositionId.HasValue && ownerLookup.TryGetValue(d.OwnerCompositionId.Value, out var label)
                    ? label
                    : $"'{d.Name}'"));
            errors.Add($"Cannot delete template '{template.Name}': it is the base of {derivatives.Count} derived template(s) used in {details}{(derivatives.Count > 10 ? "..." : "")}. Remove those compositions first.");
        }

        // Check 3: Other templates compose it directly (e.g., pre-Phase-3 data).
        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<bool>.Failure(string.Join(" ", errors));

        return Result<bool>.Success(true);
    }

    /// <summary>
    /// Deletes a template after checking all constraints.
    /// </summary>
    public async Task<Result<bool>> 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<bool>.Success(true);
    }
}
