using NSubstitute;
using TeleWave.Application.Common.Interfaces;
using TeleWave.Application.Metadata;
using TeleWave.Application.Metadata.SearchShows;
using TeleWave.Domain.Library;
using Xunit;

namespace TeleWave.Application.Tests.Metadata;

/// <summary>
/// Поиск кандидатов в источнике. Внешняя сеть — единственная часть метаданных, которая падает
/// регулярно, поэтому проверяется в первую очередь она.
/// </summary>
public class SearchShowMetadataTests
{
    private static SearchShowMetadataQueryHandler Handler(IMetadataProvider? provider)
    {
        var resolver = Substitute.For<IMetadataProviderResolver>();
        // Явно: NSubstitute сам подставляет заглушку вместо интерфейсного null.
        resolver.Resolve(Arg.Any<string>()).Returns(provider);
        return new SearchShowMetadataQueryHandler(resolver);
    }

    [Fact]
    public async Task ReturnsWhatProviderFound()
    {
        var provider = Substitute.For<IMetadataProvider>();
        provider
            .SearchShowsAsync("Симпсоны", ShowKind.Series, Arg.Any<CancellationToken>())
            .Returns([new MetadataCandidate("456", "The Simpsons", 1989, null, null)]);

        var result = await Handler(provider)
            .Handle(
                new SearchShowMetadataQuery("tmdb", "Симпсоны", ShowKind.Series),
                CancellationToken.None
            );

        Assert.True(result.IsSuccess);
        Assert.Equal("456", Assert.Single(result.Value).ExternalId);
    }

    [Fact]
    public async Task ReturnsNothing_ForBlankQuery_WithoutCallingProvider()
    {
        var provider = Substitute.For<IMetadataProvider>();

        var result = await Handler(provider)
            .Handle(
                new SearchShowMetadataQuery("tmdb", "  ", ShowKind.Single),
                CancellationToken.None
            );

        Assert.True(result.IsSuccess);
        Assert.Empty(result.Value);
        await provider
            .DidNotReceive()
            .SearchShowsAsync(Arg.Any<string>(), Arg.Any<ShowKind>(), Arg.Any<CancellationToken>());
    }

    [Fact]
    public async Task Fails_WhenProviderIsNotConfigured()
    {
        var result = await Handler(null)
            .Handle(
                new SearchShowMetadataQuery("tmdb", "Симпсоны", ShowKind.Series),
                CancellationToken.None
            );

        Assert.Equal(MetadataErrors.ProviderNotAvailable, result.Error);
    }

    [Fact]
    public async Task Fails_WhenSourceIsUnreachableOrRejectsTheKey()
    {
        var provider = Substitute.For<IMetadataProvider>();
        provider
            .SearchShowsAsync(Arg.Any<string>(), Arg.Any<ShowKind>(), Arg.Any<CancellationToken>())
            .Returns<IReadOnlyList<MetadataCandidate>>(_ => throw new HttpRequestException("401"));

        var result = await Handler(provider)
            .Handle(
                new SearchShowMetadataQuery("tmdb", "Симпсоны", ShowKind.Series),
                CancellationToken.None
            );

        // Сетевой сбой и неверный ключ — управляемая ошибка, а не исключение наружу.
        Assert.Equal(MetadataErrors.ProviderRequestFailed, result.Error);
    }
}
