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

namespace TeleWave.Application.Metadata.FindMissingSeasons;

public sealed class FindMissingSeasonsQueryHandler(
    IAppDbContext dbContext,
    IMetadataProviderResolver resolver
) : IQueryHandler<FindMissingSeasonsQuery, Result<MissingSeasonsReport>>
{
    public async Task<Result<MissingSeasonsReport>> Handle(
        FindMissingSeasonsQuery query,
        CancellationToken cancellationToken
    )
    {
        var show = await dbContext
            .Shows.AsNoTracking()
            .Include(s => s.Episodes)
            .FirstOrDefaultAsync(s => s.Id == query.ShowId, cancellationToken);
        if (show is null)
            return Result.Failure<MissingSeasonsReport>(ShowErrors.NotFound);

        // Сезоны есть только у сериала — см. FindMissingEpisodesQueryHandler.
        if (show.Kind != ShowKind.Series)
            return Result.Failure<MissingSeasonsReport>(MetadataErrors.EpisodesForSeriesOnly);

        if (string.IsNullOrEmpty(show.MetadataExternalId))
            return Result.Failure<MissingSeasonsReport>(MetadataErrors.NoLinkedSource);

        var provider = show.MetadataProvider is { } key ? resolver.Resolve(key) : null;
        if (provider is null)
            return Result.Failure<MissingSeasonsReport>(MetadataErrors.ProviderNotAvailable);

        // Имена файлов — чтобы распознать сезон у серий, где номера ещё не проставлены.
        var assetIds = show.Episodes.Select(e => e.MediaAssetId).Distinct().ToList();
        var names = await dbContext
            .MediaAssets.AsNoTracking()
            .Where(a => assetIds.Contains(a.Id))
            .Select(a => new { a.Id, a.OriginalFileName })
            .ToDictionaryAsync(a => a.Id, a => a.OriginalFileName, cancellationToken);

        var loaded = LoadedEpisodeNumbers.GroupBySeason(show, names).Keys.ToHashSet();
        var known = await provider.GetSeasonNumbersAsync(
            show.MetadataExternalId,
            cancellationToken
        );

        // Источник молчит — отдаём только загруженное: считать все сезоны отсутствующими или,
        // наоборот, лишними было бы одинаково неверно.
        return Result.Success(
            new MissingSeasonsReport(
                known.Count > 0 ? known.Count : null,
                [.. loaded.Order()],
                [.. known.Where(s => !loaded.Contains(s))],
                known.Count > 0 ? [.. loaded.Where(s => !known.Contains(s)).Order()] : []
            )
        );
    }
}
