using Baya.Application.Contracts.Common;
using Baya.Application.Contracts.Persistence;
using Baya.Application.Contracts.Search;
using Baya.Application.Features.Identity.Commands.SetNurseAcceptingBookings;
using Baya.Application.Features.Identity.Commands.UpsertNurseProfile;
using Baya.Application.Models.Identity;
using Baya.Domain.Entities.Identity;
using Baya.Domain.Entities.User;
using NSubstitute;
using NSubstitute.ReturnsExtensions;

namespace Baya.Test.Foundation.Identity;

public class NurseProfileHandlersTests
{
    private readonly ICurrentUser _currentUser = Substitute.For<ICurrentUser>();
    private readonly IUnitOfWork _unitOfWork = Substitute.For<IUnitOfWork>();
    private readonly INurseProfileRepository _repo = Substitute.For<INurseProfileRepository>();
    private readonly ISearchIndexMaintainer _searchIndex = Substitute.For<ISearchIndexMaintainer>();

    public NurseProfileHandlersTests()
    {
        _currentUser.UserId.Returns(7);
        _currentUser.Roles.Returns([RoleNames.Nurse]);
        _unitOfWork.NurseProfileRepository.Returns(_repo);
    }

    [Fact]
    public async Task Upsert_NoExistingProfile_CreatesUnverifiedAndCommits()
    {
        _repo.GetByUserIdAsync(7, Arg.Any<CancellationToken>()).ReturnsNull();
        _repo.GetMineAsync(7, Arg.Any<CancellationToken>())
            .Returns(new NurseProfileDto(1, "bio", 3, "BSc", "Nursing", "[]", false, false, 0m, 0, 0, null));
        var handler = new UpsertNurseProfileCommandHandler(_currentUser, _unitOfWork);

        var result = await handler.Handle(new UpsertNurseProfileCommand("bio", 3, "BSc", "Nursing", "[]"), CancellationToken.None);

        Assert.True(result.IsSuccess);
        Assert.False(result.Result.IsVerified);
        await _repo.Received(1).AddAsync(
            Arg.Is<NurseProfile>(p => p.UserId == 7 && !p.IsVerified && !p.IsAcceptingBookings),
            Arg.Any<CancellationToken>());
        await _unitOfWork.Received(1).CommitAsync();
    }

    [Fact]
    public async Task Upsert_NonNurseRole_IsForbidden()
    {
        _currentUser.Roles.Returns([RoleNames.Customer]);
        var handler = new UpsertNurseProfileCommandHandler(_currentUser, _unitOfWork);

        var result = await handler.Handle(new UpsertNurseProfileCommand("bio", 3, "BSc", "Nursing", "[]"), CancellationToken.None);

        Assert.True(result.IsForbidden);
        await _repo.DidNotReceive().AddAsync(Arg.Any<NurseProfile>(), Arg.Any<CancellationToken>());
        await _unitOfWork.DidNotReceive().CommitAsync();
    }

    [Fact]
    public async Task SetAcceptingBookings_NoProfile_IsNotFound()
    {
        _repo.GetByUserIdAsync(7, Arg.Any<CancellationToken>()).ReturnsNull();
        var handler = new SetNurseAcceptingBookingsCommandHandler(_currentUser, _unitOfWork, _searchIndex);

        var result = await handler.Handle(new SetNurseAcceptingBookingsCommand(true), CancellationToken.None);

        Assert.True(result.IsNotFound);
        await _unitOfWork.DidNotReceive().CommitAsync();
    }

    [Fact]
    public async Task SetAcceptingBookings_ExistingProfile_TogglesWithoutTouchingVerified()
    {
        var profile = new NurseProfile { UserId = 7 };
        _repo.GetByUserIdAsync(7, Arg.Any<CancellationToken>()).Returns(profile);
        var handler = new SetNurseAcceptingBookingsCommandHandler(_currentUser, _unitOfWork, _searchIndex);

        var result = await handler.Handle(new SetNurseAcceptingBookingsCommand(true), CancellationToken.None);

        Assert.True(result.IsSuccess);
        Assert.True(profile.IsAcceptingBookings);
        Assert.False(profile.IsVerified);
        await _unitOfWork.Received(1).CommitAsync();
    }
}
