#nullable enable
using Baya.Application.Contracts.Common;
using Baya.Application.Contracts.Configuration;
using Baya.Application.Contracts.Payments;
using Baya.Application.Contracts.Persistence;
using Baya.Application.Models.Bnpl;
using Baya.Application.Models.Common;
using Baya.Domain.Entities.Bnpl;
using Baya.Domain.Entities.Booking;
using Baya.Domain.Entities.Payments;
using Mediator;

namespace Baya.Application.Features.Bnpl.Queries.CheckBnplEligibility;

internal sealed class CheckBnplEligibilityQueryHandler(
    ICurrentUser currentUser,
    IUnitOfWork unitOfWork,
    IBnplProviderResolver providerResolver,
    IPlatformConfig platformConfig)
    : IRequestHandler<CheckBnplEligibilityQuery, OperationResult<BnplEligibilityDto>>
{
    public async ValueTask<OperationResult<BnplEligibilityDto>> Handle(CheckBnplEligibilityQuery request, CancellationToken cancellationToken)
    {
        if (currentUser.UserId is not { } userId)
            return OperationResult<BnplEligibilityDto>.UnauthorizedResult("Not authenticated.");

        var customerId = await unitOfWork.CustomerProfileRepository.GetProfileIdByUserIdAsync(userId, cancellationToken);
        if (customerId is null)
            return OperationResult<BnplEligibilityDto>.NotFoundResult("Booking request not found.");

        // Tenancy: only the owning customer may finance; any other caller must not learn the request exists.
        var ctx = await unitOfWork.BnplRepository.GetOrderContextAsync(request.BookingRequestId, cancellationToken);
        if (ctx is null || ctx.CustomerId != customerId)
            return OperationResult<BnplEligibilityDto>.NotFoundResult("Booking request not found.");

        if (await unitOfWork.PaymentRepository.HasSucceededTransactionForRequestAsync(request.BookingRequestId, cancellationToken))
            return OperationResult<BnplEligibilityDto>.ConflictResult("This booking has already been paid.");
        if (ctx.Status != BookingRequestStatus.AcceptedAwaitingPayment)
            return OperationResult<BnplEligibilityDto>.ConflictResult("This request is not awaiting payment.");

        var provider = providerResolver.Resolve(request.ProviderCode);
        if (provider is null)
            return OperationResult<BnplEligibilityDto>.FailureResult("provider_code", "The BNPL provider is not available.");

        var gatewayId = await unitOfWork.PaymentRepository.GetActiveGatewayIdAsync(PaymentGatewayType.Bnpl, cancellationToken);
        if (gatewayId is null)
            return OperationResult<BnplEligibilityDto>.FailureResult("No active BNPL gateway is configured.");

        // The D3 inputs (national id / mobile / consent) feed the provider credit inquiry; the mock uses only the
        // mobile today (the KYC step is deferred). Prefer the supplied mobile, else the account mobile.
        var inquiryMobile = string.IsNullOrWhiteSpace(request.Mobile) ? ctx.CustomerMobile : request.Mobile;
        var eligibility = await provider.CheckEligibilityAsync(inquiryMobile, ctx.GrossIrr, cancellationToken);

        var merchantOfRecord = await platformConfig.GetConfig<string>("bnpl_merchant_of_record", cancellationToken);
        var bnpl = await BnplOrderInitializer.EnsureAsync(
            unitOfWork, ctx, gatewayId.Value, request.ProviderCode, merchantOfRecord ?? "platform",
            ctx.GrossIrr, "IRR", currentUser.IpAddress, cancellationToken);

        bnpl.EligibilityStatus = eligibility.EligibilityStatus;
        bnpl.InstallmentCount = (byte)eligibility.InstallmentCount;
        await unitOfWork.CommitAsync();

        var isEligible = eligibility.EligibilityStatus == BnplEligibilityStatus.Eligible;
        return OperationResult<BnplEligibilityDto>.SuccessResult(new BnplEligibilityDto(
            eligibility.EligibilityStatus,
            isEligible,
            eligibility.InstallmentCount,
            eligibility.PlanSummary,
            eligibility.CreditCeilingIrr?.ToString()));
    }
}
