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

namespace TeleWave.Application.Library.GetShow;

public sealed class GetShowQueryHandler(IAppDbContext dbContext)
    : IQueryHandler<GetShowQuery, Result<ShowDto>>
{
    public async Task<Result<ShowDto>> Handle(
        GetShowQuery query,
        CancellationToken cancellationToken
    )
    {
        var show = await dbContext
            .Shows.AsNoTracking()
            .Include(s => s.Episodes)
            .Include(s => s.Genres)
            // Сиблинги: серии × жанры перемножились бы в одном запросе (см. ListShowsQueryHandler).
            .AsSplitQuery()
            .FirstOrDefaultAsync(s => s.Id == query.Id, cancellationToken);
        if (show is null)
            return Result.Failure<ShowDto>(ShowErrors.NotFound);

        var genreIds = show.Genres.Select(g => g.GenreId).ToList();
        var genreNames = await dbContext
            .Genres.AsNoTracking()
            .Where(g => genreIds.Contains(g.Id))
            .Select(g => new
            {
                g.Id,
                g.Name,
                g.SortOrder,
            })
            .ToListAsync(cancellationToken);

        var genreDtos = genreNames
            .OrderByDescending(g => show.Genres.First(sg => sg.GenreId == g.Id).IsPrimary)
            .ThenBy(g => g.SortOrder)
            .Select(g => new ShowGenreDto(
                g.Id,
                g.Name,
                show.Genres.First(sg => sg.GenreId == g.Id).IsPrimary
            ))
            .ToList();

        var episodes = show.Episodes.OrderBy(e => e.Position).ToList();
        var assetIds = episodes.Select(e => e.MediaAssetId).ToList();
        var assets = await dbContext
            .MediaAssets.AsNoTracking()
            .Where(a => assetIds.Contains(a.Id))
            .Select(a => new
            {
                a.Id,
                a.OriginalFileName,
                a.Status,
                a.Duration,
            })
            .ToDictionaryAsync(a => a.Id, cancellationToken);

        var episodeDtos = episodes
            .Select(e =>
            {
                assets.TryGetValue(e.MediaAssetId, out var asset);
                return new EpisodeDto(
                    e.Id,
                    e.MediaAssetId,
                    e.Position,
                    asset?.OriginalFileName,
                    asset?.Status,
                    asset?.Duration?.TotalSeconds,
                    e.Season,
                    e.Episode,
                    e.Title,
                    e.Overview,
                    e.StillImageId,
                    e.AirDate
                );
            })
            .ToList();

        // Сортировка идёт до сборки DTO: по свойству record'а EF порядок не переводит и падает
        // на компиляции запроса («The LINQ expression could not be translated»).
        var collections = await dbContext
            .CollectionItems.AsNoTracking()
            .Where(i => i.ShowId == show.Id)
            .Join(
                dbContext.Collections.AsNoTracking(),
                item => item.CollectionId,
                collection => collection.Id,
                (item, collection) =>
                    new
                    {
                        collection.Id,
                        collection.Name,
                        item.Position,
                    }
            )
            .OrderBy(c => c.Name)
            .Select(c => new ShowCollectionRefDto(c.Id, c.Name, c.Position))
            .ToListAsync(cancellationToken);

        return Result.Success(
            new ShowDto(
                show.Id,
                show.Name,
                show.OriginalName,
                show.Kind,
                show.Audience,
                show.Description,
                show.MetadataProvider,
                show.MetadataExternalId,
                show.Year,
                show.PosterImageId,
                episodeDtos,
                genreDtos,
                collections
            )
        );
    }
}
