#nullable enable
using System.Globalization;
using Baya.Application.Contracts.Common;
using Baya.Application.Contracts.Persistence;
using Baya.Application.Contracts.Search;
using Baya.Application.Models.Catalog;
using Baya.Application.Models.Common;
using Baya.Domain.Entities.User;
using Mediator;

namespace Baya.Application.Features.Variants.Commands.UpdateVariant;

internal sealed class UpdateVariantCommandHandler(ICurrentUser currentUser, IUnitOfWork unitOfWork, ISearchIndexMaintainer searchIndex)
    : IRequestHandler<UpdateVariantCommand, OperationResult<VariantDto>>
{
    public async ValueTask<OperationResult<VariantDto>> Handle(UpdateVariantCommand request, CancellationToken cancellationToken)
    {
        if (currentUser.UserId is not { } userId)
            return OperationResult<VariantDto>.UnauthorizedResult("Not authenticated.");

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

        var nurseId = await unitOfWork.NurseProfileRepository.GetProfileIdByUserIdAsync(userId, cancellationToken);
        if (nurseId is not { } nid)
            return OperationResult<VariantDto>.NotFoundResult("Variant not found.");

        // Tenancy: a non-owned/absent id resolves to null → not-found (existence is not leaked).
        var variant = await unitOfWork.NurseServiceVariantRepository.GetOwnedAsync(request.Id, nid, cancellationToken);
        if (variant is null)
            return OperationResult<VariantDto>.NotFoundResult("Variant not found.");

        variant.Price = long.Parse(request.Price, NumberStyles.None, CultureInfo.InvariantCulture);
        variant.PriceUnit = request.PriceUnit;
        variant.SessionCount = request.SessionCount;
        if (!string.IsNullOrWhiteSpace(request.DisplayName))
            variant.DisplayName = request.DisplayName.Trim();

        // Price/category changes must reach the search projection in the same transaction.
        await searchIndex.ReindexVariantAsync(variant, cancellationToken);
        await unitOfWork.CommitAsync();

        // Re-project with resolved labels for the response (the option-set is unchanged).
        var dto = await unitOfWork.NurseServiceVariantRepository.GetOwnedProjectedAsync(request.Id, nid, cancellationToken);
        return dto is null
            ? OperationResult<VariantDto>.NotFoundResult("Variant not found.")
            : OperationResult<VariantDto>.SuccessResult(dto);
    }
}
