#nullable enable
using Baya.Application.Contracts.Payments;
using Baya.Domain.Entities.Bnpl;
using Microsoft.Extensions.Options;

namespace Baya.Infrastructure.CrossCutting.Seams;

/// <summary>
/// A deterministic, network-free mock <see cref="IBnplProvider"/> that drives the full SnappPay-superset verb
/// set — <c>eligible → token_issued → verified → settled → reverted/cancelled</c> — with no external call. One
/// instance stands in for every <c>provider_code</c> (all Iranian provider-financed BNPLs behave the same);
/// a real system maps each code to its concrete adapter (SnappPay OAuth/eligible/token/verify/settle/revert,
/// Digipay UPG ticket/verify/deliver/refund). The mock commission % is <see cref="BnplOptions.CommissionRate"/>,
/// so the settle returns <c>order − commission</c> and the handler reads the commission from the response, never
/// hardcoded. A real adapter reads the actual deducted amount from each settlement.
/// </summary>
public sealed class MockBnplProvider(IOptions<SeamOptions> options) : IBnplProvider
{
    private readonly BnplOptions _options = options.Value.Bnpl;

    public ValueTask<BnplEligibilityResult> CheckEligibilityAsync(string customerMobile, long orderAmountIrr, CancellationToken cancellationToken = default)
    {
        var status = string.Equals(customerMobile, _options.NotEligibleMobile, StringComparison.Ordinal)
            ? BnplEligibilityStatus.NotEligible
            : orderAmountIrr > _options.CreditCeilingIrr
                ? BnplEligibilityStatus.CeilingExceeded
                : BnplEligibilityStatus.Eligible;

        return ValueTask.FromResult(new BnplEligibilityResult(
            status, InstallmentCount: 4, CreditCeilingIrr: _options.CreditCeilingIrr,
            PlanSummary: "4 interest-free installments, 0% interest, provider-financed."));
    }

    public ValueTask<BnplTokenResult> CreatePaymentTokenAsync(string customerMobile, long orderAmountIrr, string idempotencyKey, CancellationToken cancellationToken = default)
    {
        if (_options.ForceFailure)
            return ValueTask.FromResult(new BnplTokenResult(PaymentProviderStatus.Failed, string.Empty, string.Empty, null));

        var token = $"mock-bnpl-token-{orderAmountIrr}-{idempotencyKey}";
        return ValueTask.FromResult(new BnplTokenResult(
            PaymentProviderStatus.Succeeded, token,
            RedirectUrl: $"https://mock-bnpl.local/checkout/{token}",
            ExternalTransactionId: $"mock-bnpl-order-{idempotencyKey}"));
    }

    public ValueTask<BnplVerifyResult> VerifyAsync(string externalPaymentToken, long expectedOrderAmountIrr, CancellationToken cancellationToken = default)
        => ValueTask.FromResult(new BnplVerifyResult(
            PaymentProviderStatus.Succeeded, expectedOrderAmountIrr, $"mock-bnpl-verify-{externalPaymentToken}"));

    public ValueTask<BnplSettleResult> SettleAsync(string externalPaymentToken, long orderAmountIrr, string idempotencyKey, CancellationToken cancellationToken = default)
    {
        // The merchant discount is read from the (mock) settlement — commission %, never hardcoded in a handler.
        var commission = (long)Math.Round(orderAmountIrr * _options.CommissionRate, MidpointRounding.AwayFromZero);
        var settled = orderAmountIrr - commission;

        // Settlement timing is contract-defined and not instant — the mock can model it null (deferred) or now.
        DateTime? settledAt = _options.SettlementInstant ? DateTime.UtcNow : null;

        return ValueTask.FromResult(new BnplSettleResult(
            PaymentProviderStatus.Succeeded, settled, commission, settledAt, $"mock-bnpl-settle-{idempotencyKey}"));
    }

    public ValueTask<BnplStatusResult> GetStatusAsync(string externalPaymentToken, CancellationToken cancellationToken = default)
        => ValueTask.FromResult(new BnplStatusResult("settled"));

    public ValueTask<BnplRevertResult> CancelAsync(string externalPaymentToken, string idempotencyKey, CancellationToken cancellationToken = default)
        => Reversal(externalPaymentToken, idempotencyKey);

    public ValueTask<BnplRevertResult> RevertAsync(string providerOrderReference, long amountIrr, string idempotencyKey, CancellationToken cancellationToken = default)
        => Reversal(providerOrderReference, idempotencyKey);

    public ValueTask<BnplRevertResult> UpdateAsync(string providerOrderReference, long newAmountIrr, string idempotencyKey, CancellationToken cancellationToken = default)
        => Reversal(providerOrderReference, idempotencyKey);

    private ValueTask<BnplRevertResult> Reversal(string providerOrderReference, string idempotencyKey)
    {
        if (_options.ForceFailure)
            return ValueTask.FromResult(new BnplRevertResult(PaymentProviderStatus.Failed, null, null));

        return ValueTask.FromResult(new BnplRevertResult(
            PaymentProviderStatus.Succeeded,
            ExternalRevertReference: $"mock-bnpl-revert-{providerOrderReference}-{idempotencyKey}",
            ProviderCommissionReversedAmount: _options.ReverseProviderCommission ? 0 : null));
    }
}
