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

namespace Baya.Application.Features.Payouts.Queries.GetNurseEarningsBalance;

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

        if (currentUser.Roles?.Contains(RoleNames.Nurse) != true)
            return OperationResult<NurseEarningsBalanceDto>.ForbiddenResult("Only a nurse can read their earnings.");

        var nurseId = await unitOfWork.NurseProfileRepository.GetProfileIdByUserIdAsync(userId, cancellationToken);
        if (nurseId is not { } nid)
            return OperationResult<NurseEarningsBalanceDto>.NotFoundResult("No nurse profile exists yet.");

        var now = dateTimeProvider.UtcNow.UtcDateTime;
        var earnings = await unitOfWork.PayoutRepository.GetNurseEarningsAsync(nid, now, cancellationToken);

        long pending = 0, eligible = 0;
        foreach (var e in earnings)
        {
            var amount = long.Parse(e.NursePayoutAmount, CultureInfo.InvariantCulture);
            if (e.State == NurseEarningsState.Pending) pending += amount;
            else if (e.State == NurseEarningsState.Eligible) eligible += amount;
        }

        var paidTotal = await unitOfWork.PayoutRepository.GetPaidNetTotalAsync(nid, cancellationToken);
        var clawbackOutstanding = await unitOfWork.PayoutRepository.GetPendingClawbackSumAsync(nid, cancellationToken);
        // The authoritative, SIGNED net payable — the ledger sum over nurse_payable (may be negative). Never clamped.
        var net = await unitOfWork.PaymentRepository.GetNursePayableBalanceAsync(nid, cancellationToken);

        return OperationResult<NurseEarningsBalanceDto>.SuccessResult(new NurseEarningsBalanceDto(
            pending.ToString(CultureInfo.InvariantCulture),
            eligible.ToString(CultureInfo.InvariantCulture),
            paidTotal.ToString(CultureInfo.InvariantCulture),
            clawbackOutstanding.ToString(CultureInfo.InvariantCulture),
            net.ToString(CultureInfo.InvariantCulture)));
    }
}
