#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.SubmitCredentialDetails;

internal sealed class SubmitCredentialDetailsCommandHandler(
    ICurrentUser currentUser,
    IUnitOfWork unitOfWork)
    : IRequestHandler<SubmitCredentialDetailsCommand, OperationResult<VerificationStatusDto>>
{
    public async ValueTask<OperationResult<VerificationStatusDto>> Handle(SubmitCredentialDetailsCommand 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 credential details.");

        var profile = await unitOfWork.NurseProfileRepository.GetByUserIdAsync(userId, cancellationToken);
        if (profile is null)
            return OperationResult<VerificationStatusDto>.NotFoundResult("No nurse profile exists yet.");

        var nurseId = profile.Id;
        var holderName = string.IsNullOrWhiteSpace(request.HolderName)
            ? await unitOfWork.VerificationRepository.GetNurseIdentityNameAsync(nurseId, cancellationToken) ?? string.Empty
            : request.HolderName;

        // Specialties are the nurse's professional facets — persisted on the profile (they feed the public
        // profile + search facets), not on the credential registry.
        if (request.Specialties is not null)
            profile.SpecializationsJson = JsonCodeList.Serialize(request.Specialties) ?? "[]";

        await UpsertCredentialAsync(nurseId, CredentialTypes.InoMembership, request.InoNumber, holderName, request, cancellationToken);

        if (!string.IsNullOrWhiteSpace(request.LicenseNumber))
            await UpsertCredentialAsync(nurseId, CredentialTypes.MohCompetencyLicense, request.LicenseNumber, holderName, request, cancellationToken);

        await unitOfWork.CommitAsync();

        var status = await unitOfWork.VerificationRepository.GetStatusForNurseAsync(nurseId, cancellationToken);
        return status is null
            ? OperationResult<VerificationStatusDto>.NotFoundResult("No verification exists yet.")
            : OperationResult<VerificationStatusDto>.SuccessResult(status);
    }

    private async Task UpsertCredentialAsync(
        long nurseId, string credentialType, string number, string holderName,
        SubmitCredentialDetailsCommand request, CancellationToken cancellationToken)
    {
        var existing = await unitOfWork.VerificationRepository.GetTrackedCredentialAsync(nurseId, credentialType, cancellationToken);
        if (existing is null)
        {
            await unitOfWork.VerificationRepository.AddCredentialAsync(new NurseCredential
            {
                NurseId = nurseId,
                CredentialType = credentialType,
                CredentialNumber = number,
                HolderNameSnapshot = holderName,
                IssuingAuthority = request.IssuingAuthority ?? string.Empty,
                IssuedAt = request.IssuedAt,
                ExpiresAt = request.ExpiresAt,
                VerificationMethod = VerificationMethods.Manual
            }, cancellationToken);
            return;
        }

        // The nurse resubmitted — refresh the structured details; admin verification is unchanged.
        existing.CredentialNumber = number;
        existing.HolderNameSnapshot = holderName;
        if (request.IssuingAuthority is not null)
            existing.IssuingAuthority = request.IssuingAuthority;
        if (request.IssuedAt is not null)
            existing.IssuedAt = request.IssuedAt;
        if (request.ExpiresAt is not null)
            existing.ExpiresAt = request.ExpiresAt;
    }
}
