using ErsatzTV.Application.Search;
using ErsatzTV.Core.Interfaces.Search;
using ErsatzTV.Core.Search;
using NSubstitute;
using NUnit.Framework;
using Shouldly;

namespace ErsatzTV.Tests.Application.Search;

[TestFixture]
public class QuerySearchIndexAllItemsHandlerTests
{
    private ISearchIndex _searchIndex = null!;
    private QuerySearchIndexAllItemsHandler _handler = null!;

    [SetUp]
    public void SetUp()
    {
        _searchIndex = Substitute.For<ISearchIndex>();

        // Default every kind's search to an empty page so unconfigured kinds don't NRE.
        _searchIndex.Search(
                Arg.Any<string>(),
                Arg.Any<string>(),
                Arg.Any<int>(),
                Arg.Any<int>(),
                Arg.Any<CancellationToken>())
            .Returns(new SearchResult([], 0));

        _handler = new QuerySearchIndexAllItemsHandler(_searchIndex);
    }

    [Test]
    public async Task Handle_Should_Translate_Page_To_Skip_And_Limit()
    {
        await _handler.Handle(new QuerySearchIndexAllItems("star", 2, 50), CancellationToken.None);

        // Every one of the ten media kinds is queried with skip = pageNum * pageSize, limit = pageSize.
        await _searchIndex.Received(10).Search(
            Arg.Any<string>(),
            string.Empty,
            100,
            50,
            Arg.Any<CancellationToken>());
        await _searchIndex.Received(1).Search(
            Arg.Is<string>(q => q.Contains("type:movie")),
            string.Empty,
            100,
            50,
            Arg.Any<CancellationToken>());
    }

    [Test]
    public async Task Handle_Should_Return_Ids_And_Per_Kind_Totals()
    {
        _searchIndex.Search(
                Arg.Is<string>(q => q.Contains("type:movie")),
                Arg.Any<string>(),
                Arg.Any<int>(),
                Arg.Any<int>(),
                Arg.Any<CancellationToken>())
            .Returns(new SearchResult(
                [new SearchItem("movie", 1), new SearchItem("movie", 2)],
                7));

        SearchResultAllItemsViewModel result =
            await _handler.Handle(new QuerySearchIndexAllItems("star", 0, 500), CancellationToken.None);

        // The bucket holds this page's ids; the total reflects the full hit count (SearchResult.TotalCount),
        // which is what lets a client page to completeness.
        result.MovieIds.ShouldBe(new List<int> { 1, 2 });
        result.Totals.MovieCount.ShouldBe(7);

        // A kind with no hits returns an empty bucket and a zero total.
        result.ShowIds.ShouldBeEmpty();
        result.Totals.ShowCount.ShouldBe(0);
    }
}
