using LiteCqrs; using Microsoft.EntityFrameworkCore; using TeleWave.Application.Common.Interfaces; using TeleWave.Application.Common.Models; using TeleWave.Application.Programming.Groups; using TeleWave.Domain.Programming; namespace TeleWave.Application.Programming.Templates.Layers; public sealed class CreateLayerCommandHandler(IAppDbContext dbContext) : ICommandHandler> { public async Task> Handle( CreateLayerCommand command, CancellationToken cancellationToken ) { var template = await LayerLoader.ByTemplateAsync( dbContext, command.TemplateId, cancellationToken ); if (template is null) return Result.Failure(TemplateErrors.NotFound); var layer = template.AddLayer(command.Name, command.Priority); template.MarkChanged(); return Result.Success(layer.Id); } } public sealed class UpdateLayerCommandHandler(IAppDbContext dbContext) : ICommandHandler { public async Task Handle( UpdateLayerCommand command, CancellationToken cancellationToken ) { var template = await LayerLoader.ByLayerAsync( dbContext, command.LayerId, cancellationToken ); var layer = template?.FindLayer(command.LayerId); if (template is null || layer is null) return Result.Failure(TemplateErrors.LayerNotFound); layer.Update( command.Name, command.Priority, command.Applicability?.ToJson(), command.IsEnabled ); template.MarkChanged(); return Result.Success(); } } public sealed class DeleteLayerCommandHandler(IAppDbContext dbContext) : ICommandHandler { public async Task Handle( DeleteLayerCommand command, CancellationToken cancellationToken ) { var template = await LayerLoader.ByLayerAsync( dbContext, command.LayerId, cancellationToken ); var layer = template?.FindLayer(command.LayerId); if (template is null || layer is null) return Result.Failure(TemplateErrors.LayerNotFound); if (layer.IsBackground) return Result.Failure(TemplateErrors.BackgroundLayerCannotBeDeleted); template.RemoveLayer(command.LayerId); template.MarkChanged(); return Result.Success(); } } public sealed class UpdateTemplateCommandHandler( IAppDbContext dbContext, InterstitialGroups interstitials ) : ICommandHandler { public async Task Handle( UpdateTemplateCommand command, CancellationToken cancellationToken ) { var template = await dbContext.ScheduleTemplates.FirstOrDefaultAsync( t => t.Id == command.TemplateId, cancellationToken ); if (template is null) return Result.Failure(TemplateErrors.NotFound); if (command.FallbackGroupId is { } groupId) { if (!await dbContext.Groups.AnyAsync(g => g.Id == groupId, cancellationToken)) return Result.Failure(TemplateErrors.GroupNotFound); // Аварийная группа закрывает каждую паузу: остаток слота, добор до якоря, пустой повтор. // Из роликов она превращает всё это в рекламу — ровно то, что видно в расписании // строками «аварийный запас» подряд. if (await interstitials.IsInterstitialAsync(groupId, cancellationToken)) return Result.Failure(TemplateErrors.InterstitialFallbackGroup); } template.Rename(command.Name); template.SetFallbackGroup(command.FallbackGroupId); template.SetDefaultJunction(command.DefaultJunctionId); template.SetRules(command.Rules?.ToJson()); template.MarkChanged(); return Result.Success(); } } /// Загрузка шаблона со слоями и слотами: любая правка слоя меняет ревизию всего шаблона. internal static class LayerLoader { public static Task ByTemplateAsync( IAppDbContext dbContext, Guid templateId, CancellationToken cancellationToken ) => dbContext .ScheduleTemplates.Include(t => t.Layers) .ThenInclude(l => l.Slots) .AsSplitQuery() .FirstOrDefaultAsync(t => t.Id == templateId, cancellationToken); public static Task ByLayerAsync( IAppDbContext dbContext, Guid layerId, CancellationToken cancellationToken ) => dbContext .ScheduleTemplates.Include(t => t.Layers) .ThenInclude(l => l.Slots) .AsSplitQuery() .FirstOrDefaultAsync(t => t.Layers.Any(l => l.Id == layerId), cancellationToken); }