using JSMR.Application.Circles.Queries.Search;
using JSMR.Application.Common.Search;
using JSMR.Infrastructure.Data;
using JSMR.Infrastructure.Data.Repositories.Circles;
using Shouldly;
using SortDirection = JSMR.Application.Common.Search.SortDirection;

namespace JSMR.Tests.Search.Circle;

public class CircleSearchProviderTests(CircleSearchProviderFixture fixture) : IClassFixture<CircleSearchProviderFixture>
{
    private async Task<SearchResult<CircleSearchItem>> SearchAsync(SearchOptions<CircleSearchCriteria, CircleSortField> options)
    {
        AppDbContext context = fixture.DbContext!;
        CircleSearchProvider provider = new(context);

        return await provider.SearchAsync(options, TestContext.Current.CancellationToken);
    }

    [Fact]
    public async Task Filter_None()
    {
        var options = new SearchOptions<CircleSearchCriteria, CircleSortField>()
        {
            Criteria = new()
            {
                
            }
        };

        var result = await SearchAsync(options);

        result.Items.Length.ShouldBe(4);
        result.TotalItems.ShouldBe(4);
    }

    [Fact]
    public async Task Filter_By_Status_Not_Blacklisted()
    {
        var options = new SearchOptions<CircleSearchCriteria, CircleSortField>()
        {
            Criteria = new()
            {
                Status = CircleStatus.NotBlacklisted
            }
        };

        var result = await SearchAsync(options);

        result.Items.Length.ShouldBe(3);
        result.TotalItems.ShouldBe(3);
        result.Items.ShouldNotContain(item => item.Blacklisted);
    }

    [Fact]
    public async Task Filter_By_Status_Favorited()
    {
        var options = new SearchOptions<CircleSearchCriteria, CircleSortField>()
        {
            Criteria = new()
            {
                Status = CircleStatus.Favorited
            }
        };

        var result = await SearchAsync(options);

        result.Items.Length.ShouldBe(1);
        result.TotalItems.ShouldBe(1);
        result.Items.ShouldAllBe(item => item.Favorite);
    }

    [Fact]
    public async Task Filter_By_Status_Blacklisted()
    {
        var options = new SearchOptions<CircleSearchCriteria, CircleSortField>()
        {
            Criteria = new()
            {
                Status = CircleStatus.Blacklisted
            }
        };

        var result = await SearchAsync(options);

        result.Items.Length.ShouldBe(1);
        result.TotalItems.ShouldBe(1);
        result.Items.ShouldAllBe(item => item.Blacklisted);
    }

    [Fact]
    public async Task Filter_By_Status_Spam()
    {
        var options = new SearchOptions<CircleSearchCriteria, CircleSortField>()
        {
            Criteria = new()
            {
                Status = CircleStatus.Spam
            }
        };

        var result = await SearchAsync(options);

        result.Items.Length.ShouldBe(1);
        result.TotalItems.ShouldBe(1);
        result.Items.ShouldAllBe(item => item.Spam);
    }

    [Fact]
    public async Task Filter_By_Name_Circle_Name()
    {
        var options = new SearchOptions<CircleSearchCriteria, CircleSortField>()
        {
            Criteria = new()
            {
                Name = "Dreams"
            }
        };

        var result = await SearchAsync(options);

        result.Items.Length.ShouldBe(2);
        result.TotalItems.ShouldBe(2);
        result.Items.ShouldAllBe(item => item.Name.Contains("Dreams", StringComparison.OrdinalIgnoreCase));
    }

    [Fact]
    public async Task Filter_By_Name_Circle_Id()
    {
        var options = new SearchOptions<CircleSearchCriteria, CircleSortField>()
        {
            Criteria = new()
            {
                Name = "003"
            }
        };

        var result = await SearchAsync(options);

        result.Items.Length.ShouldBe(1);
        result.TotalItems.ShouldBe(1);
        result.Items.ShouldContain(item => item.MakerId.Contains("003", StringComparison.OrdinalIgnoreCase));
    }

    [Fact]
    public async Task Sort_By_Name_Descending()
    {
        var options = new SearchOptions<CircleSearchCriteria, CircleSortField>()
        {
            SortOptions = [new(CircleSortField.Name, SortDirection.Descending)]
        };

        var result = await SearchAsync(options);

        result.Items.Length.ShouldBe(4);
        result.TotalItems.ShouldBe(4);
        result.Items[0].Name.ShouldBe("Sweet Dreams");
        result.Items[1].Name.ShouldBe("Nightmare Fuel");
        result.Items[2].Name.ShouldBe("Good Dreams");
        result.Items[3].Name.ShouldBe("Garbage Studio");
    }

    [Fact]
    public async Task Sort_By_Favorite_Ascending()
    {
        var options = new SearchOptions<CircleSearchCriteria, CircleSortField>()
        {
            SortOptions = [new(CircleSortField.Favorite, SortDirection.Ascending)]
        };

        var result = await SearchAsync(options);

        result.Items.Length.ShouldBe(4);
        result.TotalItems.ShouldBe(4);
        result.Items[0].Name.ShouldBe("Sweet Dreams");
        result.Items[1].Name.ShouldBe("Garbage Studio");
    }

    [Fact]
    public async Task Sort_By_Blacklisted_Ascending()
    {
        var options = new SearchOptions<CircleSearchCriteria, CircleSortField>()
        {
            SortOptions = [new(CircleSortField.Blacklisted, SortDirection.Ascending)]
        };

        var result = await SearchAsync(options);

        result.Items.Length.ShouldBe(4);
        result.TotalItems.ShouldBe(4);
        result.Items[0].Name.ShouldBe("Nightmare Fuel");
        result.Items[1].Name.ShouldBe("Garbage Studio");
    }

    [Fact]
    public async Task Sort_By_Spam_Ascending()
    {
        var options = new SearchOptions<CircleSearchCriteria, CircleSortField>()
        {
            SortOptions = [new(CircleSortField.Spam, SortDirection.Ascending)]
        };

        var result = await SearchAsync(options);

        result.Items.Length.ShouldBe(4);
        result.TotalItems.ShouldBe(4);
        result.Items[0].Name.ShouldBe("Garbage Studio");
        result.Items[1].Name.ShouldBe("Good Dreams");
    }
}