#nullable enable
using Baya.Application.Models.Common;
using Baya.Application.Models.Payouts;
using Baya.Domain.Entities.Payouts;
using Baya.Domain.Entities.Refunds;

namespace Baya.Application.Contracts.Persistence;

/// <summary>
/// The payouts aggregate — the weekly batch, its per-nurse payouts, and the anti-double-pay booking links. Reads
/// project to DTOs (<c>AsNoTracking</c> + <c>.Select</c>); writes load tracked rows. The payout <b>ledger legs</b>
/// are appended through <see cref="IPaymentRepository.AddLedgerEntriesAsync"/> (b10's helper) — this repo owns the
/// payout rows and the money facts a batch is built from. Money is IRR <c>long</c>. The
/// <c>nurse_payout_booking_links.booking_id</c> UNIQUE is the authoritative one-payout-per-booking backstop; the
/// eligibility predicate's "not already linked" filter is the fast first line.
/// </summary>
public interface IPayoutRepository
{
    // ---- eligibility (preview + build) ----

    /// <summary>The payout-eligible, unpaid bookings for the window: <c>status='completed'</c> AND
    /// <c>dispute_window_ends_at &lt; now</c> AND no active refund AND not already in a link row (and, when
    /// <paramref name="requireBnplSettlement"/> is set, its BNPL provider settlement is received). One row per
    /// booking (nurse + this booking's payout portion) — grouped by nurse in the build handler.</summary>
    Task<IReadOnlyList<EligibleBookingRow>> GetEligibleBookingsAsync(
        DateOnly periodStart, DateOnly periodEnd, DateTime now, bool requireBnplSettlement, CancellationToken cancellationToken);

    /// <summary>The same eligibility set as a per-nurse preview (paginated), netting pending clawbacks and
    /// flagging any nurse without a verified primary IBAN.</summary>
    Task<PagedResult<EligibleNurseEarningsDto>> GetEligiblePreviewAsync(
        DateOnly periodStart, DateOnly periodEnd, DateTime now, bool requireBnplSettlement, int page, int pageSize, CancellationToken cancellationToken);

    /// <summary>The nurse's verified primary payout account (<c>is_primary=1 AND is_verified=1 AND
    /// matched_national_id=1</c>) — the first-payout gate. Null when the nurse has none (the payout is skipped
    /// with a recorded reason). The IBAN is decrypted by the EF converter on projection.</summary>
    Task<VerifiedPayoutAccount?> GetVerifiedPrimaryAccountAsync(long nurseId, CancellationToken cancellationToken);

    /// <summary>The nurse's display name (for the batch preview/detail), or null.</summary>
    Task<IReadOnlyDictionary<long, string>> GetNurseNamesAsync(IReadOnlyList<long> nurseIds, CancellationToken cancellationToken);

    /// <summary>The sum of the nurse's <c>pending</c> clawbacks (IRR) — netted (capped at earnings) into a payout
    /// at build time.</summary>
    Task<long> GetPendingClawbackSumAsync(long nurseId, CancellationToken cancellationToken);

    /// <summary>The nurse's <c>pending</c> clawbacks, oldest first, tracked — the execute step marks them
    /// <c>recovered</c> (with <c>recovered_in_payout_id</c> + <c>resolved_at</c>) up to the payout's frozen
    /// <c>clawback_applied_irr</c>.</summary>
    Task<IReadOnlyList<NurseClawback>> GetPendingClawbacksAsync(long nurseId, CancellationToken cancellationToken);

    // ---- writes ----

    Task AddBatchAsync(NursePayoutBatch batch, CancellationToken cancellationToken);

    /// <summary>The tracked batch with its payouts + links — loaded by execute/retry to drive the transfers and
    /// post the ledger. Null when absent.</summary>
    Task<NursePayoutBatch?> GetTrackedBatchAsync(long batchId, CancellationToken cancellationToken);

    /// <summary>A single tracked payout (with its batch) — for retry/mark-failed. Null when absent.</summary>
    Task<NursePayout?> GetTrackedPayoutAsync(long payoutId, CancellationToken cancellationToken);

    /// <summary>Whether a payout already has a posted ledger group — makes the execute ledger post idempotent so
    /// a retried execute never double-posts.</summary>
    Task<bool> LedgerGroupExistsForPayoutAsync(long payoutId, CancellationToken cancellationToken);

    // ---- reads (admin + nurse) ----

    Task<PagedResult<PayoutBatchDto>> ListBatchesAsync(string? status, int page, int pageSize, CancellationToken cancellationToken);

    Task<PayoutBatchDetailDto?> GetBatchDetailAsync(long batchId, int page, int pageSize, CancellationToken cancellationToken);

    /// <summary>The nurse's own payouts (tenancy-scoped), most recent first, projected + paginated. Masked IBAN.</summary>
    Task<PagedResult<NursePayoutHistoryDto>> GetNurseHistoryAsync(long nurseId, int page, int pageSize, CancellationToken cancellationToken);

    /// <summary>Every completed/closed booking the nurse earned, each with its <b>server-derived</b> money-state
    /// (<c>pending|eligible|paid|clawback_applied</c>) from <c>bookings.status</c> + <c>dispute_window_ends_at</c>
    /// + the payout link + any clawback. The handler filters by state, sums the buckets, and paginates.</summary>
    Task<IReadOnlyList<NurseEarningsItemDto>> GetNurseEarningsAsync(long nurseId, DateTime now, CancellationToken cancellationToken);

    /// <summary>Lifetime total (IRR) of the nurse's <c>paid</c> payouts' net — the <c>paidTotalIrr</c> bucket.</summary>
    Task<long> GetPaidNetTotalAsync(long nurseId, CancellationToken cancellationToken);

    /// <summary>A nurse-scoped payout detail (payout + batch window + covered bookings). Null when the payout is
    /// absent or not the nurse's — never leak another nurse's payout.</summary>
    Task<NursePayoutDetailDto?> GetNursePayoutDetailAsync(long payoutId, long nurseId, CancellationToken cancellationToken);
}

/// <summary>The verified primary account a payout snapshots — the account id and the decrypted IBAN (frozen into
/// the encrypted <c>iban_snapshot</c> at build time).</summary>
public record VerifiedPayoutAccount(long BankAccountId, string Iban);
