using Baya.Application.Contracts.Common;
using Baya.Application.Contracts.Persistence;
using Baya.Application.Features.Verification.Commands.SubmitVerification;
using Baya.Application.Models.Identity;
using Baya.Application.Models.Verification;
using Baya.Domain.Entities.Identity;
using Baya.Domain.Entities.User;
using Baya.Domain.Entities.Verification;
using NSubstitute;
using NSubstitute.ReturnsExtensions;
using static Baya.Test.Foundation.Verification.VerificationTestSupport;

namespace Baya.Test.Foundation.Verification;

public class SubmitNurseVerificationHandlerTests
{
    private readonly ICurrentUser _currentUser = Substitute.For<ICurrentUser>();
    private readonly IUnitOfWork _unitOfWork = Substitute.For<IUnitOfWork>();
    private readonly IVerificationRepository _verif = Substitute.For<IVerificationRepository>();
    private readonly INurseProfileRepository _nurses = Substitute.For<INurseProfileRepository>();
    private readonly IDateTimeProvider _clock = Substitute.For<IDateTimeProvider>();

    public SubmitNurseVerificationHandlerTests()
    {
        _currentUser.UserId.Returns(7);
        _currentUser.Roles.Returns([RoleNames.Nurse]);
        _unitOfWork.VerificationRepository.Returns(_verif);
        _unitOfWork.NurseProfileRepository.Returns(_nurses);
        _clock.UtcNow.Returns(new DateTimeOffset(2026, 7, 1, 12, 0, 0, TimeSpan.Zero));
        _nurses.GetIdentityContextByUserIdAsync(7, Arg.Any<CancellationToken>())
            .Returns(new NurseIdentityContext(42, "0012345678"));
        _nurses.GetTrackedByIdAsync(42, Arg.Any<CancellationToken>()).Returns(new NurseProfile());
        _verif.GetActiveRequiredStepTypesAsync(Arg.Any<CancellationToken>())
            .Returns(new List<VerificationStepType>
            {
                StepType(1, VerificationStepTypeCodes.IdentityKyc, automated: true),
                StepType(2, VerificationStepTypeCodes.MohCompetencyLicense, automated: false)
            });
        _verif.GetStatusForNurseAsync(42, Arg.Any<CancellationToken>())
            .Returns(new VerificationStatusDto("pending", false, ["identity_kyc", "moh_competency_license"], []));
    }

    private SubmitNurseVerificationCommandHandler Handler() => new(_currentUser, _unitOfWork, _clock);

    [Fact]
    public async Task Submit_NewVerification_SeedsOneStepPerRequiredType_WithAutomationSnapshot()
    {
        _verif.GetTrackedByNurseIdAsync(42, Arg.Any<CancellationToken>()).ReturnsNull();

        var result = await Handler().Handle(new SubmitNurseVerificationCommand(), CancellationToken.None);

        Assert.True(result.IsSuccess);
        await _verif.Received(1).AddVerificationAsync(
            Arg.Is<NurseVerification>(v =>
                v.NurseId == 42
                && v.Status == VerificationStatus.Pending
                && v.Steps.Count == 2
                && v.Steps.Any(s => s.StepTypeId == 1 && s.IsAutomated)
                && v.Steps.Any(s => s.StepTypeId == 2 && !s.IsAutomated)),
            Arg.Any<CancellationToken>());
        await _unitOfWork.Received(1).CommitAsync();
    }

    [Fact]
    public async Task Submit_ExistingVerification_IsIdempotent_OnlyAddsMissingSteps()
    {
        var existing = new NurseVerification { NurseId = 42, Status = VerificationStatus.Pending, SubmittedAt = _clock.UtcNow };
        existing.Steps.Add(Step(1, StepType(1, VerificationStepTypeCodes.IdentityKyc, automated: true), VerificationStepStatus.Pending));
        _verif.GetTrackedByNurseIdAsync(42, Arg.Any<CancellationToken>()).Returns(existing);

        var result = await Handler().Handle(new SubmitNurseVerificationCommand(), CancellationToken.None);

        Assert.True(result.IsSuccess);
        // Only the missing MoH step is added; the identity step is not duplicated.
        Assert.Equal(2, existing.Steps.Count);
        Assert.Single(existing.Steps, s => s.StepTypeId == 2);
        await _verif.DidNotReceive().AddVerificationAsync(Arg.Any<NurseVerification>(), Arg.Any<CancellationToken>());
    }

    [Fact]
    public async Task Submit_NonNurse_IsForbidden()
    {
        _currentUser.Roles.Returns([RoleNames.Customer]);

        var result = await Handler().Handle(new SubmitNurseVerificationCommand(), CancellationToken.None);

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