using LiteCqrs;
using Microsoft.EntityFrameworkCore;
using TeleWave.Application.Common.Interfaces;
using TeleWave.Application.Common.Models;

namespace TeleWave.Application.Programming.Templates.Junctions;

public sealed class DeleteJunctionCommandHandler(IAppDbContext dbContext)
    : ICommandHandler<DeleteJunctionCommand, Result>
{
    public async Task<Result> Handle(
        DeleteJunctionCommand command,
        CancellationToken cancellationToken
    )
    {
        var junction = await JunctionLoader.LoadAsync(
            dbContext,
            command.JunctionId,
            cancellationToken
        );
        if (junction is null)
            return Result.Failure(TemplateErrors.JunctionNotFound);

        // Стык общий: слот чужого канала, ссылающийся на удалённый стык, молча остался бы без врезок.
        var usedBySlot = await dbContext.Slots.AnyAsync(
            s => s.JunctionBetweenId == junction.Id || s.JunctionAfterId == junction.Id,
            cancellationToken
        );
        var usedByDefault = await dbContext.ScheduleTemplates.AnyAsync(
            t => t.DefaultJunctionId == junction.Id,
            cancellationToken
        );

        // Факт и адрес — разные вопросы: имя канала берётся по цепочке связей, и её неполнота
        // не должна превращать запрет в разрешение.
        if (usedBySlot || usedByDefault)
            return Result.Failure(
                TemplateErrors.JunctionInUse(await UsedByAsync(junction.Id, cancellationToken))
            );

        dbContext.JunctionTemplates.Remove(junction);
        return Result.Success();
    }

    /// <summary>Кто держит стык: слоты каналов и каналы, где он выбран стыком по умолчанию.</summary>
    private async Task<string> UsedByAsync(Guid junctionId, CancellationToken cancellationToken)
    {
        var slots = await (
            from slot in dbContext.Slots
            join layer in dbContext.GridLayers on slot.LayerId equals layer.Id
            join template in dbContext.ScheduleTemplates on layer.TemplateId equals template.Id
            join channel in dbContext.Channels on template.ChannelId equals channel.Id
            where slot.JunctionBetweenId == junctionId || slot.JunctionAfterId == junctionId
            select channel.Name
        )
            .Distinct()
            .ToListAsync(cancellationToken);

        var byDefault = await (
            from template in dbContext.ScheduleTemplates
            join channel in dbContext.Channels on template.ChannelId equals channel.Id
            where template.DefaultJunctionId == junctionId
            select channel.Name
        )
            .Distinct()
            .ToListAsync(cancellationToken);

        return UsageText.Join(
            UsageText.Part("слоты каналов", slots),
            UsageText.Part("стык по умолчанию у каналов", byDefault)
        );
    }
}
