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

namespace TeleWave.Application.Media.ListMedia;

public sealed class ListMediaAssetsQueryHandler(IAppDbContext dbContext)
    : IQueryHandler<ListMediaAssetsQuery, PagedList<MediaAssetDto>>
{
    public async Task<PagedList<MediaAssetDto>> Handle(
        ListMediaAssetsQuery query,
        CancellationToken cancellationToken
    )
    {
        // Сгенерированные системой ассеты (ТВ-заставки) не показываем в библиотеке медиа.
        var q = dbContext.MediaAssets.AsNoTracking().Where(x => x.Source != MediaSource.Generated);

        if (query.Statuses.Count > 0)
            q = q.Where(x => query.Statuses.Contains(x.Status));

        if (!string.IsNullOrWhiteSpace(query.Search))
        {
            // ToLower().Contains переводится в LIKE lower(...) — регистронезависимо и без привязки к
            // Npgsql-специфичному ILike (Application не ссылается на провайдер).
            var term = query.Search.Trim().ToLower();
            // CA1862 (Contains со StringComparison) здесь неприменим: это дерево выражений EF, а
            // перегрузку с StringComparison провайдер в SQL не переводит — будет исключение в рантайме.
#pragma warning disable CA1862
            q = q.Where(x => x.OriginalFileName.ToLower().Contains(term));
#pragma warning restore CA1862
        }

        var total = await q.CountAsync(cancellationToken);

        // Сортировка по выбранному столбцу (в SQL, до пагинации); вторичный ключ — Id для стабильного
        // порядка при равенстве значений (иначе страницы «дрожат»).
        var ordered = (query.Sort?.ToLowerInvariant()) switch
        {
            "name" => query.Desc
                ? q.OrderByDescending(x => x.OriginalFileName)
                : q.OrderBy(x => x.OriginalFileName),
            "status" => query.Desc ? q.OrderByDescending(x => x.Status) : q.OrderBy(x => x.Status),
            "duration" => query.Desc
                ? q.OrderByDescending(x => x.Duration)
                : q.OrderBy(x => x.Duration),
            "resolution" => query.Desc
                ? q.OrderByDescending(x => x.Width)
                : q.OrderBy(x => x.Width),
            "processing" => query.Desc
                ? q.OrderByDescending(x => x.ProcessingDuration)
                : q.OrderBy(x => x.ProcessingDuration),
            "created" => query.Desc
                ? q.OrderByDescending(x => x.CreatedAt)
                : q.OrderBy(x => x.CreatedAt),
            _ => q.OrderByDescending(x => x.CreatedAt),
        };

        // Маппинг в памяти: MediaAssetDto.From обращается к TimeSpan.TotalSeconds, который EF в SQL
        // не переводит. Страница ограничена pageSize, поэтому материализация сущностей безопасна.
        var entities = await ordered
            .ThenBy(x => x.Id)
            .Skip((query.Page - 1) * query.PageSize)
            .Take(query.PageSize)
            .ToListAsync(cancellationToken);

        var items = entities.Select(MediaAssetDto.From).ToList();
        return new PagedList<MediaAssetDto>(items, total, query.Page, query.PageSize);
    }
}
