#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.Identity;
using Baya.Domain.Entities.Identity;
using Baya.Domain.Entities.User;
using Mediator;

namespace Baya.Application.Features.Identity.Commands.AddNurseBankAccount;

internal sealed class AddNurseBankAccountCommandHandler(
    ICurrentUser currentUser,
    IUnitOfWork unitOfWork,
    IFieldEncryptor fieldEncryptor,
    IBankAccountOwnershipVerifier ownershipVerifier)
    : IRequestHandler<AddNurseBankAccountCommand, OperationResult<NurseBankAccountDto>>
{
    public async ValueTask<OperationResult<NurseBankAccountDto>> Handle(AddNurseBankAccountCommand request, CancellationToken cancellationToken)
    {
        if (currentUser.UserId is not { } userId)
            return OperationResult<NurseBankAccountDto>.UnauthorizedResult("Not authenticated.");

        if (currentUser.Roles?.Contains(RoleNames.Nurse) != true)
            return OperationResult<NurseBankAccountDto>.ForbiddenResult("Only a nurse can add a payout account.");

        var iban = Sheba.Normalize(request.Iban);
        if (iban is null)
            return OperationResult<NurseBankAccountDto>.FailureResult(nameof(request.Iban), "A valid Iranian IBAN (شبا) is required.");

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

        // Deterministic hash is the duplicate guard — the UNIQUE(iban_hash) index is the DB backstop.
        var ibanHash = fieldEncryptor.Hash(iban);
        if (await unitOfWork.NurseBankAccountRepository.IbanHashExistsAsync(ibanHash, cancellationToken))
            return OperationResult<NurseBankAccountDto>.FailureResult(nameof(request.Iban), "This IBAN is already registered.");

        var isFirst = !await unitOfWork.NurseBankAccountRepository.HasAnyAsync(context.NurseProfileId, cancellationToken);

        var account = new NurseBankAccount
        {
            NurseId = context.NurseProfileId,
            BankName = request.BankName,
            AccountHolderName = request.AccountHolderName,
            Iban = iban,
            IbanHash = ibanHash,
            IsPrimary = isFirst
        };

        var inquiry = await ownershipVerifier.VerifyOwnershipAsync(iban, context.NationalId, cancellationToken);
        account.ApplyOwnershipInquiry(inquiry.MatchedNationalId, inquiry.AccountHolderFromBank, inquiry.VendorRef);

        await unitOfWork.NurseBankAccountRepository.AddAsync(account, cancellationToken);
        await unitOfWork.CommitAsync();

        return OperationResult<NurseBankAccountDto>.SuccessResult(new NurseBankAccountDto(
            account.Id,
            account.BankName,
            Mask.IbanTail(iban),
            account.IsPrimary,
            account.IsVerified,
            account.MatchedNationalId));
    }
}
