using Baya.Application.Contracts.Common;
using Baya.Application.Contracts.Persistence;
using Baya.Application.Features.Reviews.Queries.GetReviewEligibility;
using Baya.Application.Models.Reviews;
using Baya.Domain.Entities.Booking;
using NSubstitute;

namespace Baya.Test.Foundation.Reviews;

public class GetReviewEligibilityHandlerTests
{
    private readonly ICurrentUser _currentUser = Substitute.For<ICurrentUser>();
    private readonly IUnitOfWork _unitOfWork = Substitute.For<IUnitOfWork>();
    private readonly ICustomerProfileRepository _customers = Substitute.For<ICustomerProfileRepository>();
    private readonly IReviewRepository _reviews = Substitute.For<IReviewRepository>();

    private const int UserId = 7;
    private const long CustomerId = 100;
    private const long BookingId = 5;

    public GetReviewEligibilityHandlerTests()
    {
        _currentUser.UserId.Returns(UserId);
        _unitOfWork.CustomerProfileRepository.Returns(_customers);
        _unitOfWork.ReviewRepository.Returns(_reviews);
        _customers.GetProfileIdByUserIdAsync(UserId, Arg.Any<CancellationToken>()).Returns(CustomerId);
    }

    [Fact]
    public async Task Eligibility_CompletedOwnedUnreviewed_CanReview()
    {
        _reviews.GetReviewableBookingAsync(BookingId, Arg.Any<CancellationToken>())
            .Returns(new ReviewableBooking(BookingId, CustomerId, 1, BookingStatus.Completed));
        _reviews.ExistsForBookingAsync(BookingId, Arg.Any<CancellationToken>()).Returns(false);

        var result = await Handle();

        Assert.True(result.IsSuccess);
        Assert.True(result.Result.CanReview);
        Assert.Null(result.Result.Reason);
    }

    [Fact]
    public async Task Eligibility_NotCompleted_ReasonNotCompleted()
    {
        _reviews.GetReviewableBookingAsync(BookingId, Arg.Any<CancellationToken>())
            .Returns(new ReviewableBooking(BookingId, CustomerId, 1, BookingStatus.InProgress));

        var result = await Handle();

        Assert.False(result.Result.CanReview);
        Assert.Equal(ReviewEligibilityDto.ReasonNotCompleted, result.Result.Reason);
    }

    [Fact]
    public async Task Eligibility_AlreadyReviewed_ReasonAlreadyReviewed()
    {
        _reviews.GetReviewableBookingAsync(BookingId, Arg.Any<CancellationToken>())
            .Returns(new ReviewableBooking(BookingId, CustomerId, 1, BookingStatus.Closed));
        _reviews.ExistsForBookingAsync(BookingId, Arg.Any<CancellationToken>()).Returns(true);

        var result = await Handle();

        Assert.False(result.Result.CanReview);
        Assert.Equal(ReviewEligibilityDto.ReasonAlreadyReviewed, result.Result.Reason);
    }

    [Fact]
    public async Task Eligibility_ForeignBooking_ReasonNotOwner()
    {
        _reviews.GetReviewableBookingAsync(BookingId, Arg.Any<CancellationToken>())
            .Returns(new ReviewableBooking(BookingId, CustomerProfileId: 999, 1, BookingStatus.Completed));

        var result = await Handle();

        Assert.False(result.Result.CanReview);
        Assert.Equal(ReviewEligibilityDto.ReasonNotOwner, result.Result.Reason);
    }

    private ValueTask<Baya.Application.Models.Common.OperationResult<ReviewEligibilityDto>> Handle()
        => new GetReviewEligibilityQueryHandler(_currentUser, _unitOfWork)
            .Handle(new GetReviewEligibilityQuery(BookingId), CancellationToken.None);
}
