#nullable enable
using Baya.Domain.Common;

namespace Baya.Domain.Entities.Payouts;

/// <summary>
/// One row per nurse per batch — the exact amount transferred, the frozen IBAN snapshot, and the bank transfer
/// reference for reconciliation. The nurse's earnings for the window are <see cref="GrossEarningsIrr"/>; any
/// <c>pending</c> clawback the nurse owes back is netted into <see cref="ClawbackAppliedIrr"/> so the platform
/// never overpays a nurse with a receivable.
/// <para>
/// <b>Invariant:</b> <see cref="NetAmountIrr"/> = <see cref="GrossEarningsIrr"/> −
/// <see cref="ClawbackAppliedIrr"/>; all amounts ≥ 0; <see cref="NetAmountIrr"/> ≥ 0 (a clawback exceeding
/// earnings nets to <b>zero this batch</b>, the remainder staying <c>pending</c> for the next — never a negative
/// transfer). <see cref="Amount"/> is what actually moves; it equals <see cref="NetAmountIrr"/> on success.
/// Money is IRR <c>BIGINT</c>, no floats. <see cref="IbanSnapshot"/> is encrypted at rest and frozen at build
/// time from the nurse's verified primary account. Paid-ness is derived from a
/// <c>nurse_payout_booking_links</c> row + the ledger movement — never a boolean flag.
/// </para>
/// </summary>
public class NursePayout : BaseEntity<long>, IAuditable
{
    public long BatchId { get; set; }
    public NursePayoutBatch Batch { get; set; } = null!;

    public long NurseId { get; set; }

    /// <summary>The verified primary account paid (FK <c>nurse_bank_accounts</c>).</summary>
    public long BankAccountId { get; set; }

    /// <summary>The account's IBAN, frozen at build time and <b>encrypted at rest</b> through the field encryptor.
    /// <see cref="AuditRedactedAttribute"/> so the audit-log diff records a redaction marker, never the plaintext IBAN.</summary>
    [AuditRedacted]
    public string IbanSnapshot { get; set; } = null!;

    /// <summary>Σ eligible booking payouts for the window (IRR).</summary>
    public long GrossEarningsIrr { get; set; }

    /// <summary>Pending clawbacks netted this batch (IRR, ≥ 0, capped at <see cref="GrossEarningsIrr"/>).</summary>
    public long ClawbackAppliedIrr { get; set; }

    /// <summary>Derived: <see cref="GrossEarningsIrr"/> − <see cref="ClawbackAppliedIrr"/> (IRR, ≥ 0).</summary>
    public long NetAmountIrr { get; set; }

    /// <summary>Actually transferred net (IRR) — equals <see cref="NetAmountIrr"/> on success.</summary>
    public long Amount { get; set; }

    public int BookingCount { get; set; }

    /// <summary>Guarded — a <see cref="PayoutStatus"/> code, mutated only through the mark-* methods.</summary>
    public string Status { get; private set; } = PayoutStatus.Pending;

    /// <summary>The bank track id (PAYA/SATNA), set at submit — kept for reconciliation.</summary>
    public string? TransferReference { get; private set; }

    public DateTime? PaidAt { get; private set; }

    public string? FailureReason { get; private set; }

    public DateTimeOffset? DeletedAt { get; set; }

    public ICollection<NursePayoutBookingLink> BookingLinks { get; set; } = new List<NursePayoutBookingLink>();

    public bool CanTransitionTo(string target) => PayoutStatusTransitions.CanTransition(Status, target);

    private void Transition(string target)
    {
        if (!PayoutStatusTransitions.CanTransition(Status, target))
            throw new InvalidOperationException($"Illegal payout transition {Status} → {target}.");
        Status = target;
    }

    /// <summary>Records the bank track id and transitions <c>pending|failed → submitted</c>. Clears any prior
    /// failure so a retry starts clean.</summary>
    public void MarkSubmitted(string transferReference)
    {
        Transition(PayoutStatus.Submitted);
        TransferReference = transferReference;
        FailureReason = null;
    }

    /// <summary>Confirms the transfer and transitions <c>submitted → paid</c> — the irreversible movement.</summary>
    public void MarkPaid(DateTime now)
    {
        Transition(PayoutStatus.Paid);
        PaidAt = now;
    }

    /// <summary>Records a rail rejection and transitions to <c>failed</c> (from pending or submitted).</summary>
    public void MarkFailed(string reason)
    {
        Transition(PayoutStatus.Failed);
        FailureReason = reason;
    }
}
