using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using TeleWave.Application.Broadcast.Scheduling;
using TeleWave.Application.Common.Interfaces;
using TeleWave.Application.Streaming;
using TeleWave.Domain.Broadcast;

namespace TeleWave.Application.Broadcast.Bumpers;

/// <summary>
/// Восстанавливает <see cref="BumperRenderSpec"/> по кэш-строке заставки: планировщик сохранил только
/// ссылки (канал/блок/подблок/пара шоу), а рендеру нужны названия шоу и абсолютные пути к звуку,
/// постеру и фону. Вынесено из фонового рендерера: чтение и сборка — работа слоя приложения,
/// воркер лишь крутит ffmpeg.
/// </summary>
public sealed class BumperSpecLoader(
    IAppDbContext dbContext,
    IBumperTemplateStorage bumperStorage,
    IImageStore imageStore,
    IOptions<BumperOptions> bumperOptions,
    IOptions<StreamingOptions> streamingOptions
)
{
    private readonly BumperOptions _bumper = bumperOptions.Value;
    private readonly int _segmentSeconds = Math.Max(1, streamingOptions.Value.SegmentSeconds);

    /// <summary>Спецификация заставки для ассета, либо null если восстановить её уже нельзя.</summary>
    public async Task<BumperRenderSpec?> LoadAsync(
        Guid assetId,
        CancellationToken cancellationToken
    )
    {
        var cache = await dbContext
            .BumperAssets.AsNoTracking()
            .Where(b => b.MediaAssetId == assetId)
            .OrderByDescending(b => b.CreatedAt)
            .FirstOrDefaultAsync(cancellationToken);
        if (cache is null)
            return null;

        var channel = await dbContext
            .Channels.AsNoTracking()
            .Include(c => c.BumperTemplates)
                .ThenInclude(t => t.Variants)
            .AsSplitQuery()
            .FirstOrDefaultAsync(c => c.Id == cache.ChannelId, cancellationToken);
        var template = channel?.BumperTemplates.FirstOrDefault(t => t.Id == cache.TemplateId);
        var variant = template?.Variants.FirstOrDefault(v => v.Id == cache.VariantId);
        if (channel is null || template is null || variant is null)
            return null;

        var names = await dbContext
            .Shows.AsNoTracking()
            .Where(s => s.Id == cache.FromShowId || s.Id == cache.ToShowId)
            .Select(s => new { s.Id, s.Name })
            .ToDictionaryAsync(s => s.Id, s => s.Name, cancellationToken);

        // Постер шоу-получателя как фон — только для «Сейчас/Далее».
        string? posterPath = null;
        if (variant.Kind == BumperTextKind.NowNext)
            posterPath = await ResolveShowPosterAsync(cache.ToShowId, cancellationToken);

        var bgPath = await ResolveImagePathAsync(template.BackgroundImageId, cancellationToken);
        var aligned = BumperDuration.Aligned(
            BumperDuration.TemplateSeconds(template),
            _segmentSeconds
        );

        return BumperSpecFactory.Build(
            _bumper,
            channel.BumperFont,
            template,
            variant,
            aligned,
            new BumperSpecInputs(
                names.GetValueOrDefault(cache.FromShowId, "…"),
                names.GetValueOrDefault(cache.ToShowId, "…"),
                bumperStorage.AudioPath(template.Id, template.AudioExtension),
                posterPath,
                bgPath
            )
        );
    }

    private async Task<string?> ResolveShowPosterAsync(
        Guid showId,
        CancellationToken cancellationToken
    )
    {
        var posterImageId = await dbContext
            .Shows.AsNoTracking()
            .Where(s => s.Id == showId && s.PosterImageId != null)
            .Select(s => s.PosterImageId)
            .FirstOrDefaultAsync(cancellationToken);
        return await ResolveImagePathAsync(posterImageId, cancellationToken);
    }

    private async Task<string?> ResolveImagePathAsync(
        Guid? imageId,
        CancellationToken cancellationToken
    )
    {
        if (imageId is not { } id)
            return null;

        var ext = await dbContext
            .Images.AsNoTracking()
            .Where(i => i.Id == id)
            .Select(i => i.FileExtension)
            .FirstOrDefaultAsync(cancellationToken);
        return ext is null ? null : imageStore.ResolvePath(id, ext);
    }
}
