using NSubstitute;
using TeleWave.Application.Common.Interfaces;
using TeleWave.Application.Metadata.FindMissingEpisodes;
using TeleWave.Application.Tests.Support;
using TeleWave.Domain.Library;
using Xunit;

namespace TeleWave.Application.Tests.Metadata;

public class FindMissingEpisodesTests
{
    [Fact]
    public async Task ReportsMissingNumbers_PerLoadedSeason()
    {
        var fixture = new TestDb();
        var show = Show.Create("A", ShowKind.Series);
        show.ApplyMetadata("tmdb", "123", "desc", 2000, null);
        // Сезон 1: загружены 1 и 3 (из 5). Сезон 2: загружена только 1 (из 3).
        var e11 = show.AddEpisode(Guid.NewGuid());
        e11.SetNumbers(1, 1);
        var e13 = show.AddEpisode(Guid.NewGuid());
        e13.SetNumbers(1, 3);
        var e21 = show.AddEpisode(Guid.NewGuid());
        e21.SetNumbers(2, 1);

        await using (var seed = fixture.New())
        {
            seed.Shows.Add(show);
            await seed.SaveChangesAsync(CancellationToken.None);
        }

        var provider = Substitute.For<IMetadataProvider>();
        provider.GetSeasonEpisodeCountAsync("123", 1, Arg.Any<CancellationToken>()).Returns(5);
        provider.GetSeasonEpisodeCountAsync("123", 2, Arg.Any<CancellationToken>()).Returns(3);
        var resolver = Substitute.For<IMetadataProviderResolver>();
        resolver.Resolve("tmdb").Returns(provider);

        await using var db = fixture.New();
        var result = await new FindMissingEpisodesQueryHandler(db, resolver).Handle(
            new FindMissingEpisodesQuery(show.Id),
            CancellationToken.None
        );

        Assert.True(result.IsSuccess);
        var s1 = result.Value.Seasons.Single(x => x.Season == 1);
        Assert.Equal(5, s1.Expected);
        Assert.Equal(2, s1.Loaded);
        Assert.Equal([2, 4, 5], s1.Missing);
        var s2 = result.Value.Seasons.Single(x => x.Season == 2);
        Assert.Equal([2, 3], s2.Missing);
    }

    [Fact]
    public async Task Fails_ForNonSeries()
    {
        // У полнометражки сезонов нет, и запрос ушёл бы в /tv/{id} с идентификатором фильма.
        var fixture = new TestDb();
        var show = Show.Create("A", ShowKind.Single);
        show.ApplyMetadata("tmdb", "123", null, 2000, null);
        await using (var seed = fixture.New())
        {
            seed.Shows.Add(show);
            await seed.SaveChangesAsync(CancellationToken.None);
        }

        var resolver = Substitute.For<IMetadataProviderResolver>();
        await using var db = fixture.New();
        var result = await new FindMissingEpisodesQueryHandler(db, resolver).Handle(
            new FindMissingEpisodesQuery(show.Id),
            CancellationToken.None
        );

        Assert.False(result.IsSuccess);
    }

    [Fact]
    public async Task Fails_WhenNoLinkedSource()
    {
        var fixture = new TestDb();
        var show = Show.Create("A", ShowKind.Series);
        await using (var seed = fixture.New())
        {
            seed.Shows.Add(show);
            await seed.SaveChangesAsync(CancellationToken.None);
        }

        var resolver = Substitute.For<IMetadataProviderResolver>();
        await using var db = fixture.New();
        var result = await new FindMissingEpisodesQueryHandler(db, resolver).Handle(
            new FindMissingEpisodesQuery(show.Id),
            CancellationToken.None
        );

        Assert.False(result.IsSuccess);
    }
}
