#nullable enable
using Baya.Application.Common;
using Baya.Application.Contracts.Common;
using Baya.Application.Contracts.Persistence;
using Baya.Application.Models.Common;
using Baya.Application.Models.Verification;
using Baya.Domain.Entities.User;
using Baya.Domain.Entities.Verification;
using Mediator;

namespace Baya.Application.Features.Verification.Commands.SubmitVerification;

internal sealed class SubmitNurseVerificationCommandHandler(
    ICurrentUser currentUser,
    IUnitOfWork unitOfWork,
    IDateTimeProvider dateTimeProvider)
    : IRequestHandler<SubmitNurseVerificationCommand, OperationResult<VerificationStatusDto>>
{
    public async ValueTask<OperationResult<VerificationStatusDto>> Handle(
        SubmitNurseVerificationCommand request, CancellationToken cancellationToken)
    {
        if (currentUser.UserId is not { } userId)
            return OperationResult<VerificationStatusDto>.UnauthorizedResult("Not authenticated.");

        if (currentUser.Roles?.Contains(RoleNames.Nurse) != true)
            return OperationResult<VerificationStatusDto>.ForbiddenResult("Only a nurse can submit a verification.");

        var repo = unitOfWork.VerificationRepository;

        var context = await unitOfWork.NurseProfileRepository.GetIdentityContextByUserIdAsync(userId, cancellationToken);
        if (context is null)
            return OperationResult<VerificationStatusDto>.FailureResult("No nurse profile exists yet. Create your profile first.");

        var nurseId = context.NurseProfileId;
        var now = dateTimeProvider.UtcNow;

        var verification = await repo.GetTrackedByNurseIdAsync(nurseId, cancellationToken);
        if (verification is null)
        {
            verification = new NurseVerification { NurseId = nurseId, Status = VerificationStatus.Pending, SubmittedAt = now };
            await repo.AddVerificationAsync(verification, cancellationToken);
        }
        else if (verification.SubmittedAt is null)
        {
            verification.SubmittedAt = now;
        }

        // Seed one step per active *required* step-type, snapshotting is_automated. Idempotent: only the
        // step-types not already seeded are added, so re-submitting never duplicates a step.
        var requiredTypes = await repo.GetActiveRequiredStepTypesAsync(cancellationToken);
        var existingTypeIds = verification.Steps.Select(s => s.StepTypeId).ToHashSet();

        foreach (var stepType in requiredTypes.Where(t => !existingTypeIds.Contains(t.Id)))
        {
            verification.Steps.Add(new VerificationStep
            {
                NurseVerification = verification,
                StepTypeId = stepType.Id,
                Status = VerificationStepStatus.Pending,
                IsAutomated = stepType.IsAutomated
            });
        }

        var profile = await unitOfWork.NurseProfileRepository.GetTrackedByIdAsync(nurseId, cancellationToken);
        if (profile is null)
            return OperationResult<VerificationStatusDto>.NotFoundResult("Nurse profile not found.");

        VerificationAggregator.Finalize(verification, profile, now);

        await unitOfWork.CommitAsync();

        var status = await repo.GetStatusForNurseAsync(nurseId, cancellationToken);
        return OperationResult<VerificationStatusDto>.SuccessResult(status!);
    }
}
