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

namespace Baya.Infrastructure.CrossCutting.Seams;

/// <summary>
/// A deterministic, network-free mock <see cref="IBankTransferProvider" /> for the PAYA/SATNA payout rail. It
/// moves <b>no money</b>: every instruction gets a deterministic <c>transfer_reference</c> and settles
/// <see cref="BankTransferStatus.Paid" /> (the mock collapses the real <c>submitted → paid</c> reconciliation
/// into one step). It <b>honours</b> the <see cref="PayoutInstruction.Method" /> chosen by the handler (PAYA vs
/// SATNA by the config threshold) and echoes it back. A config switch forces a deterministic failure so the
/// <c>partially_failed</c>/retry paths are testable: <see cref="BankTransferOptions.ForceFailure" /> fails every
/// instruction (→ whole-batch failure), and <see cref="BankTransferOptions.FailIban" /> fails just that one
/// destination (→ partial failure). A real transferor (Jibit / Vandar / Sadad payout) replaces this registration
/// only — the source settlement account, per-nurse Sheba, and the reconciliation callback are its concern.
/// </summary>
public sealed class MockBankTransferProvider(IOptions<SeamOptions> options) : IBankTransferProvider
{
    private readonly BankTransferOptions _options = options.Value.BankTransfer;

    public ValueTask<PayoutBatchSubmitResult> SubmitPayoutBatchAsync(
        long payoutBatchId,
        IReadOnlyList<PayoutInstruction> instructions,
        string idempotencyKey,
        CancellationToken cancellationToken = default)
    {
        var results = new List<PayoutInstructionResult>(instructions.Count);
        foreach (var instruction in instructions)
        {
            var fail = _options.ForceFailure
                       || (!string.IsNullOrEmpty(_options.FailIban)
                           && string.Equals(instruction.Iban, _options.FailIban, StringComparison.Ordinal));

            results.Add(fail
                ? new PayoutInstructionResult(instruction.PayoutId, BankTransferStatus.Failed, null, instruction.Method, "provider_declined")
                : new PayoutInstructionResult(
                    instruction.PayoutId, BankTransferStatus.Paid,
                    TransferReference: $"mock-payout-{payoutBatchId}-{instruction.PayoutId}-{idempotencyKey}",
                    instruction.Method, FailureReason: null));
        }

        return ValueTask.FromResult(new PayoutBatchSubmitResult(
            ExternalBatchRef: $"mock-batch-{payoutBatchId}-{idempotencyKey}", results));
    }

    public ValueTask<BankTransferStatus> GetPayoutStatusAsync(string externalBatchRef, CancellationToken cancellationToken = default)
        => ValueTask.FromResult(_options.ForceFailure ? BankTransferStatus.Failed : BankTransferStatus.Paid);
}
