#nullable enable
namespace Baya.Application.Contracts.Payments;

/// <summary>
/// The Buy-Now-Pay-Later provider seam (SnappPay / Digipay / Tara / Torob Pay) — the SnappPay-superset verb set
/// that drives the full <c>eligible → token_issued → verified → settled → reverted/cancelled</c> state machine.
/// <b>One impl per <c>provider_code</c></b>, selected through <see cref="IBnplProviderResolver"/>. A BNPL order
/// is, in our books, a card payment landing net-of-fee: the <b>settle</b> returns the net amount + the provider's
/// merchant commission (read from the actual settlement, never hardcoded) + a <b>per-transaction</b>
/// <c>settled_at</c> that is never assumed instant. Money <b>always</b> flows <c>customer ↔ provider ↔
/// Balinyaar</c>; every amount is IRR <c>long</c> (Toman is converted only in a real adapter's boundary via
/// <see cref="ICurrencyNormalizer"/>); an <c>idempotencyKey</c> makes a retried settle/revert a no-op.
/// </summary>
public interface IBnplProvider
{
    /// <summary>Checks whether the customer may finance this order — records <c>eligibility_status</c>.</summary>
    ValueTask<BnplEligibilityResult> CheckEligibilityAsync(string customerMobile, long orderAmountIrr, CancellationToken cancellationToken = default);

    /// <summary>Issues the <c>external_payment_token</c> + the redirect URL that starts the customer's order.</summary>
    ValueTask<BnplTokenResult> CreatePaymentTokenAsync(string customerMobile, long orderAmountIrr, string idempotencyKey, CancellationToken cancellationToken = default);

    /// <summary>Confirms the order — echoes the amount so the handler can re-check it server-side (never trust the callback alone).</summary>
    ValueTask<BnplVerifyResult> VerifyAsync(string externalPaymentToken, long expectedOrderAmountIrr, CancellationToken cancellationToken = default);

    /// <summary>Settles the full order to Balinyaar net of the provider commission — returns the net amount, the
    /// commission (a platform expense), and the per-transaction <c>settled_at</c>.</summary>
    ValueTask<BnplSettleResult> SettleAsync(string externalPaymentToken, long orderAmountIrr, string idempotencyKey, CancellationToken cancellationToken = default);

    /// <summary>The provider's current view of the order (reconciliation/support).</summary>
    ValueTask<BnplStatusResult> GetStatusAsync(string externalPaymentToken, CancellationToken cancellationToken = default);

    /// <summary>Cancels an order before settle.</summary>
    ValueTask<BnplRevertResult> CancelAsync(string externalPaymentToken, string idempotencyKey, CancellationToken cancellationToken = default);

    /// <summary>Full reversal of a settled BNPL order back through the provider (a <c>bnpl_revert</c> refund).</summary>
    ValueTask<BnplRevertResult> RevertAsync(string providerOrderReference, long amountIrr, string idempotencyKey, CancellationToken cancellationToken = default);

    /// <summary>Partial revert — reduces the order to a strictly-lower <paramref name="newAmountIrr"/>.</summary>
    ValueTask<BnplRevertResult> UpdateAsync(string providerOrderReference, long newAmountIrr, string idempotencyKey, CancellationToken cancellationToken = default);
}

/// <summary>The eligibility outcome + plan summary the client shows (or falls back to card on).</summary>
/// <param name="EligibilityStatus">A <c>BnplEligibilityStatus</c> code (<c>eligible</c>/<c>not_eligible</c>/<c>ceiling_exceeded</c>).</param>
/// <param name="InstallmentCount">Informational plan length (default 4 — owned by the provider).</param>
/// <param name="CreditCeilingIrr">The customer's provider credit ceiling (IRR), when known.</param>
/// <param name="PlanSummary">Human copy, e.g. "4 interest-free installments, provider-financed".</param>
public sealed record BnplEligibilityResult(
    string EligibilityStatus,
    int InstallmentCount,
    long? CreditCeilingIrr,
    string PlanSummary);

/// <param name="Status">Whether a token was issued.</param>
/// <param name="ExternalPaymentToken">The deterministic token persisted for verify/settle/revert.</param>
/// <param name="RedirectUrl">Where the customer is sent to complete the BNPL order.</param>
/// <param name="ExternalTransactionId">The provider's order/txn id, when issued.</param>
public sealed record BnplTokenResult(
    PaymentProviderStatus Status,
    string ExternalPaymentToken,
    string RedirectUrl,
    string? ExternalTransactionId);

/// <param name="Status">The re-verified outcome — only <see cref="PaymentProviderStatus.Succeeded"/> confirms.</param>
/// <param name="OrderAmountIrr">The order amount the provider reports, re-checked against the stored order.</param>
/// <param name="ExternalTransactionId">The provider's order/txn id, when returned.</param>
public sealed record BnplVerifyResult(
    PaymentProviderStatus Status,
    long OrderAmountIrr,
    string? ExternalTransactionId);

/// <param name="Status">Whether the provider settled.</param>
/// <param name="SettledAmountIrr">Net of the provider commission actually received (IRR).</param>
/// <param name="BnplCommissionIrr">The provider's merchant discount (IRR) = a platform expense.</param>
/// <param name="SettledAt">The per-transaction settlement time — <b>nullable</b>, contract-defined, never assumed instant.</param>
/// <param name="ExternalTransactionId">The provider's order/txn id, when returned.</param>
public sealed record BnplSettleResult(
    PaymentProviderStatus Status,
    long SettledAmountIrr,
    long BnplCommissionIrr,
    DateTime? SettledAt,
    string? ExternalTransactionId);

/// <param name="ProviderStatus">The provider's own status string for the order.</param>
public sealed record BnplStatusResult(string ProviderStatus);

/// <summary>The outcome of a BNPL revert/update/cancel.</summary>
/// <param name="Status">Whether the provider accepted the reversal.</param>
/// <param name="ExternalRevertReference">The provider's revert id, persisted on the refund + the BNPL row.</param>
/// <param name="ProviderCommissionReversedAmount">The provider's own commission it returned — <b>nullable</b>,
/// reconciled from the response, never hardcoded (some providers keep their fee on a refund).</param>
public sealed record BnplRevertResult(
    PaymentProviderStatus Status,
    string? ExternalRevertReference,
    long? ProviderCommissionReversedAmount);
