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

namespace TeleWave.Application.Media.Delete;

public sealed class DeleteMediaAssetCommandHandler(IAppDbContext dbContext, IMediaStorage storage)
    : ICommandHandler<DeleteMediaAssetCommand, Result>
{
    public async Task<Result> Handle(
        DeleteMediaAssetCommand command,
        CancellationToken cancellationToken
    )
    {
        var asset = await dbContext.MediaAssets.FirstOrDefaultAsync(
            x => x.Id == command.Id,
            cancellationToken
        );
        if (asset is null)
            return Result.Failure(MediaErrors.NotFound);

        // Записи расписания и заполнитель канала ссылаются на ассет без внешнего ключа, поэтому БД
        // такое удаление пропустила бы — и раздача HLS упёрлась бы в отсутствующие сегменты уже
        // в эфире. Проверяем и прошедшие записи тоже: их подчищает генератор, а до тех пор строка
        // с висячей ссылкой ломает EPG и трейс.
        var inSchedule = await dbContext.ScheduleEntries.AnyAsync(
            e => e.MediaAssetId == asset.Id,
            cancellationToken
        );
        var isFiller = await dbContext.Channels.AnyAsync(
            c => c.FillerAssetId == asset.Id,
            cancellationToken
        );

        if (inSchedule || isFiller)
            return Result.Failure(
                MediaErrors.InUse(await UsedByAsync(asset.Id, cancellationToken))
            );

        // Сначала коммитим удаление в БД, потом чистим файлы (best-effort): orphan-файл на диске
        // безопаснее, чем строка БД, ссылающаяся на уже удалённые артефакты.
        var extension = asset.OriginalExtension;
        var assetId = asset.Id;
        dbContext.MediaAssets.Remove(asset);
        await dbContext.SaveChangesAsync(cancellationToken);
        await storage.DeleteAssetArtifactsAsync(assetId, extension, cancellationToken);

        return Result.Success();
    }

    /// <summary>Чем занят ассет: расписанием каких каналов и где он выбран заполнителем.</summary>
    private async Task<string> UsedByAsync(Guid assetId, CancellationToken cancellationToken)
    {
        var inSchedule = await (
            from entry in dbContext.ScheduleEntries
            join channel in dbContext.Channels on entry.ChannelId equals channel.Id
            where entry.MediaAssetId == assetId
            select channel.Name
        )
            .Distinct()
            .ToListAsync(cancellationToken);

        var asFiller = await dbContext
            .Channels.Where(c => c.FillerAssetId == assetId)
            .Select(c => c.Name)
            .ToListAsync(cancellationToken);

        return UsageText.Join(
            UsageText.Part("расписание каналов", inSchedule),
            UsageText.Part("заполнитель каналов", asFiller)
        );
    }
}
