#nullable enable
using Baya.Domain.Common;

namespace Baya.Domain.Entities.Payments;

/// <summary>
/// Every payment attempt against a booking request; the <see cref="PaymentTransactionStatus.Succeeded"/> row
/// is what triggers confirmation. It stores the full <see cref="GatewayResponseJson"/> and the Shaparak
/// <see cref="GatewayReferenceCode"/> — the definitive proof for reconciliation and chargebacks.
/// <para>
/// Two structural idempotency guards protect the money path (configured on the table, not just in a handler):
/// a filtered <c>UNIQUE(gateway_reference_code) WHERE NOT NULL</c> (Shaparak ref dedupe) and a filtered
/// <c>UNIQUE(booking_id) WHERE status='succeeded'</c> (<b>at most one capturing transaction per booking</b>).
/// The latter is the authoritative anti-double-capture backstop even if the Redis lock is lost.
/// </para>
/// <para>
/// Because a b9 <c>bookings</c> row is created only <i>on capture</i>, the attempt is opened against the
/// originating <see cref="BookingRequestId"/> and <see cref="BookingId"/> stays null until confirmation binds
/// the two together (which is also when the succeeded-unique starts guarding the booking).
/// </para>
/// </summary>
public class PaymentTransaction : BaseEntity<long>
{
    /// <summary>The originating <c>accepted_awaiting_payment</c> request this attempt pays for.</summary>
    public long BookingRequestId { get; set; }

    /// <summary>The confirmed booking, set only when this attempt succeeds and conversion creates it. Null
    /// while pending — so the filtered succeeded-unique only ever guards a captured booking.</summary>
    public long? BookingId { get; private set; }

    public long CustomerId { get; set; }
    public long GatewayId { get; set; }

    /// <summary>The charged amount (IRR) — the booking's frozen gross. No floats, ever.</summary>
    public long Amount { get; set; }

    /// <summary>Always <c>IRR</c> internally; Toman is a display concern at the provider boundary only.</summary>
    public string Currency { get; set; } = "IRR";

    /// <summary>Guarded — mutated only through <see cref="MarkSucceeded"/>/<see cref="MarkFailed"/>.</summary>
    public string Status { get; private set; } = PaymentTransactionStatus.Pending;

    public string? GatewayTransactionId { get; set; }

    /// <summary>The Shaparak reference code — definitive reconciliation/chargeback proof. Filtered-unique.</summary>
    public string? GatewayReferenceCode { get; set; }

    public string? GatewayResponseCode { get; private set; }
    public string? GatewayResponseJson { get; private set; }

    public bool IsInstallment { get; set; }
    public string? IpAddress { get; set; }
    public string? UserAgent { get; set; }

    public DateTimeOffset? DeletedAt { get; set; }

    /// <summary>Binds the succeeded capture to its booking and records the gateway response. The filtered
    /// <c>UNIQUE(booking_id) WHERE status='succeeded'</c> makes a second succeeded row for the same booking
    /// impossible — a concurrent double-confirm fails on the constraint (treated as an idempotent no-op).</summary>
    public void MarkSucceeded(long bookingId, string? responseCode, string? responseJson)
    {
        BookingId = bookingId;
        Status = PaymentTransactionStatus.Succeeded;
        GatewayResponseCode = responseCode;
        GatewayResponseJson = responseJson;
    }

    public void MarkFailed(string? responseCode, string? responseJson)
    {
        Status = PaymentTransactionStatus.Failed;
        GatewayResponseCode = responseCode;
        GatewayResponseJson = responseJson;
    }
}
