<?php

declare(strict_types=1);

namespace App\Filament\Resources\Credits\Pages;

use App\Enums\CreditAuditEvent;
use App\Filament\Resources\Credits\Actions\ArchiveCreditAction;
use App\Filament\Resources\Credits\Actions\ExtendCreditAction;
use App\Filament\Resources\Credits\Actions\ExtendWithInterestAction;
use App\Filament\Resources\Credits\Actions\RefinanceCreditAction;
use App\Filament\Resources\Credits\Actions\RenewCreditAction;
use App\Filament\Resources\Credits\Actions\RestructureCreditAction;
use App\Filament\Resources\Credits\Actions\RestructureWithCustomInstallmentsAction;
use App\Filament\Resources\Credits\Actions\SettleCreditAction;
use App\Filament\Resources\Credits\Actions\UnarchiveCreditAction;
use App\Filament\Resources\Credits\CreditResource;
use App\Filament\Resources\Payments\PaymentResource;
use App\Models\Credit;
use App\Models\Payment;
use App\Services\CreditCascadeDeletionService;
use App\Services\CreditOperationService;
use App\Services\PaymentManager;
use App\Support\Format;
use Filament\Actions\Action;
use Filament\Actions\ActionGroup;
use Filament\Actions\DeleteAction;
use Filament\Forms\Components\Textarea;
use Filament\Notifications\Notification;
use Filament\Resources\Pages\ViewRecord;
use Filament\Schemas\Schema;
use Filament\Support\Enums\Size;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\HtmlString;

/**
 * View Credit Page - Redesigned for Decision-Oriented UX.
 *
 * ACTION GROUPS:
 * 1. Primary: Registrar Pago (main CTA)
 * 2. Modificar Plazo: Extender, Prorrogar, Reestructurar (no disbursement)
 * 3. Nuevo Capital: Refinanciar, Renovar (with disbursement)
 * 4. Administrar: Editar, Duplicar, Eliminar
 */
class ViewCredit extends ViewRecord
{
    protected static string $resource = CreditResource::class;

    protected function getHeaderWidgets(): array
    {
        return [];
    }

    public function getHeading(): string
    {
        /** @var Credit $credit */
        $credit = $this->record;
        $status = $credit->getStatusLabel();

        return "Crédito #{$credit->code} — {$status}";
    }

    public function getSubheading(): ?string
    {
        $client = $this->record->client?->name ?? 'Sin cliente';
        $remaining = Format::money($this->record->remaining_balance);

        if ($this->record->isClosed()) {
            return "{$client}";
        }

        return "{$client} · Pendiente: {$remaining}";
    }

    public function infolist(Schema $schema): Schema
    {
        return CreditResource::infolist($schema);
    }

    protected function getHeaderActions(): array
    {
        // Refresh status before evaluating permissions
        $this->record->refreshStatus();
        $this->record->refresh();

        // Filament tipa `$this->record` como `Model|int|string`, así que acceder a
        // las propiedades del crédito directamente deja ciego a PHPStan.
        /** @var Credit $credit */
        $credit = $this->record;

        $isClosed = $credit->isClosed();
        $hasPayments = $credit->hasPayments();
        $isArchived = $credit->status === Credit::STATUS_ARCHIVED;
        $remaining = $this->record->remaining_balance;

        return [
            // ═══════════════════════════════════════════════════════════════════════
            // PRIMARY ACTION: Register Payment
            // The most common action - always prominent and visible
            // ═══════════════════════════════════════════════════════════════════════
            Action::make('createPayment')
                ->label('Registrar Pago')
                ->icon('heroicon-o-banknotes')
                ->color('success')
                ->size(Size::Large)
                ->url(fn () => PaymentResource::getUrl('create', [
                    'credit_id' => $this->record->id,
                ]))
                ->visible(fn () => ! $isClosed)
                ->extraAttributes([
                    'title' => 'Registrar un abono o pago del cliente',
                ]),

            // ═══════════════════════════════════════════════════════════════════════
            // GROUP: Term Operations (No Disbursement)
            // These operations modify the payment schedule WITHOUT delivering new money
            // ═══════════════════════════════════════════════════════════════════════
            ActionGroup::make([
                // Group Header (disabled, just for visual separation)
                Action::make('term_ops_header')
                    ->label('Operaciones de Plazo')
                    ->disabled()
                    ->extraAttributes(['class' => 'font-bold text-xs uppercase tracking-wider opacity-60 cursor-default']),

                ExtendCreditAction::make()
                    ->label('Extender Plazo')
                    ->modalDescription(fn () => new HtmlString($this->getExtendDescription())),

                ExtendWithInterestAction::make()
                    ->label('Prorrogar con Intereses')
                    ->modalDescription(fn () => new HtmlString($this->getExtendWithInterestDescription())),

                RestructureCreditAction::make()
                    ->label('Reestructurar Condiciones')
                    ->modalDescription(fn () => new HtmlString($this->getRestructureDescription())),

                RestructureWithCustomInstallmentsAction::make()
                    ->label('Reestructurar (Cuotas Personalizadas)')
                    ->modalDescription(fn () => new HtmlString($this->getRestructureCustomDescription())),
            ])
                ->label('Modificar Plazo')
                ->icon('heroicon-o-calendar-days')
                ->color('info')
                ->button()
                ->visible(fn () => ! $isClosed && $remaining > 0)
                ->extraAttributes([
                    'title' => 'Operaciones que modifican el plazo sin entregar dinero nuevo',
                ]),

            // ═══════════════════════════════════════════════════════════════════════
            // GROUP: Capital Operations (WITH Disbursement)
            // These operations involve delivering NEW money to the client
            // ═══════════════════════════════════════════════════════════════════════
            ActionGroup::make([
                // Group Header
                Action::make('capital_ops_header')
                    ->label('Operaciones de Capital')
                    ->disabled()
                    ->extraAttributes(['class' => 'font-bold text-xs uppercase tracking-wider opacity-60 cursor-default']),

                RefinanceCreditAction::make()
                    ->label('Refinanciar (+Capital)')
                    ->modalDescription(fn () => new HtmlString($this->getRefinanceDescription())),

                RenewCreditAction::make()
                    ->label('Renovar Crédito')
                    ->modalDescription(fn () => new HtmlString($this->getRenewDescription())),
            ])
                ->label('Nuevo Capital')
                ->icon('heroicon-o-banknotes')
                ->color('warning')
                ->button()
                ->visible(fn () => ! $isClosed)
                ->extraAttributes([
                    'title' => 'Operaciones que entregan dinero nuevo al cliente',
                ]),

            // ═══════════════════════════════════════════════════════════════════════
            // GROUP: Administrative Actions
            // Secondary operations for credit management
            // ═══════════════════════════════════════════════════════════════════════
            ActionGroup::make([
                // Group Header
                Action::make('admin_header')
                    ->label('Opciones Administrativas')
                    ->disabled()
                    ->extraAttributes(['class' => 'font-bold text-xs uppercase tracking-wider opacity-60 cursor-default']),

                SettleCreditAction::make()
                    ->label('Condonar Saldo y Cerrar'),

                // Para los créditos cuyo desenlace aún no está decidido: sale de
                // la ruta del cobrador sin cerrarlo ni condonar nada.
                ArchiveCreditAction::make(),

                UnarchiveCreditAction::make(),

                Action::make('edit')
                    ->label('Editar Crédito')
                    ->icon('heroicon-m-pencil-square')
                    ->color('warning')
                    ->visible(fn () => ! $isClosed && ! $hasPayments)
                    ->url(fn () => CreditResource::getUrl('edit', ['record' => $this->record]))
                    ->extraAttributes([
                        'title' => 'Solo disponible si no tiene pagos registrados',
                    ]),

                Action::make('duplicate')
                    ->label('Duplicar como Nuevo')
                    ->icon('heroicon-m-document-duplicate')
                    ->color('info')
                    ->visible(fn () => ! $isClosed)
                    ->requiresConfirmation()
                    ->modalIcon('heroicon-o-exclamation-triangle')
                    ->modalIconColor('warning')
                    ->modalHeading('Duplicar Crédito')
                    ->modalDescription(new HtmlString('
                        <div class="space-y-3 text-sm">
                            <div class="rounded-lg bg-warning-50 dark:bg-warning-950 p-3 text-warning-800 dark:text-warning-200">
                                <strong>⚠️ ATENCIÓN:</strong> Esta acción crea un NUEVO crédito con un NUEVO desembolso.
                            </div>
                            <p>Se copiará la configuración del crédito actual (monto, tasa, cuotas, periodicidad) a un nuevo crédito editable.</p>
                            <p><strong>El crédito actual NO se modifica.</strong></p>
                        </div>
                    '))
                    ->modalSubmitActionLabel('Sí, crear nuevo crédito')
                    ->action(fn () => $this->duplicateCredit()),

                DeleteAction::make()
                    ->label('Eliminar Crédito')
                    ->visible(fn () => ! $hasPayments)
                    // Ruta el borrado por el servicio de cascada (igual que la tabla) para
                    // limpiar TODO el rastro financiero. El delete por defecto de Filament
                    // solo hace $record->delete(): la FK nullOnDelete dejaría el Expense de
                    // desembolso DESVINCULADO (credit_id NULL) pero vivo → corrompería la caja.
                    ->schema([
                        Textarea::make('reason')
                            ->label('Motivo de la eliminación')
                            ->helperText('Obligatorio. Queda en el log de auditoría. Recuerda: eliminar devuelve el desembolso a la caja.')
                            ->required()
                            ->rows(3),
                    ])
                    ->using(function (Credit $record, array $data): void {
                        // Rastro de auditoría FUERA de la BD: los CreditAuditLog se borran en
                        // cascada con el crédito (FK cascadeOnDelete), así que el motivo se
                        // registra en el log de la aplicación antes de la baja.
                        Log::warning('Crédito eliminado (hard delete)', [
                            'credit_id' => $record->id,
                            'amount' => (float) $record->amount,
                            'user_id' => auth()->id(),
                            'reason' => $data['reason'] ?? null,
                        ]);

                        app(CreditCascadeDeletionService::class)->deleteCreditCompletely($record);
                    })
                    ->modalIcon('heroicon-o-trash')
                    ->modalIconColor('danger')
                    ->modalHeading('Eliminar Crédito Permanentemente')
                    ->modalDescription(function (): HtmlString {
                        /** @var Credit $credit */
                        $credit = $this->record;

                        return new HtmlString(
                            '<div class="space-y-3 text-sm">'.
                            '<div class="rounded-lg bg-danger-50 dark:bg-danger-950 p-3 text-danger-800 dark:text-danger-200"><strong>⛔ ACCIÓN IRREVERSIBLE</strong></div>'.
                            '<p>Esta acción eliminará permanentemente el crédito y todos sus datos asociados (cuotas, historial).</p>'.
                            '<div class="rounded-lg bg-warning-50 dark:bg-warning-950 p-3 text-warning-800 dark:text-warning-200">'.
                            '<strong>💵 Impacto en caja:</strong> al eliminar se <strong>devolverán $'.number_format((float) $credit->amount, 0, ',', '.').'</strong> a la caja (el desembolso registrado). '.
                            'Usa <strong>Eliminar</strong> solo si el dinero <strong>nunca salió</strong> (error de registro). Si el cliente ya recibió el dinero, <strong>Cancela</strong> el crédito en su lugar: mantiene el registro y <strong>no</strong> mueve la caja.'.
                            '</div>'.
                            '</div>'
                        );
                    }),
            ])
                ->label('Administrar')
                ->icon('heroicon-o-cog-6-tooth')
                ->color('gray')
                ->dropdown()
                // Un crédito archivado está "cerrado" y normalmente tiene pagos,
                // así que este grupo se ocultaba y desarchivar quedaba fuera de
                // alcance: se podía archivar pero no volver atrás. El camino de
                // vuelta tiene que existir siempre.
                ->visible(fn () => ! $isClosed || ! $hasPayments || $isArchived),
        ];
    }

    // ═══════════════════════════════════════════════════════════════════════════════
    // HELPER METHODS: Enhanced Modal Descriptions
    // ═══════════════════════════════════════════════════════════════════════════════

    private function getExtendDescription(): string
    {
        $remaining = Format::money($this->record->remaining_balance);

        return "
            <div class='space-y-3 text-sm'>
                <div class='rounded-lg bg-info-50 dark:bg-info-950 p-3'>
                    <strong>📅 ¿Qué hace?</strong>
                    <p class='mt-1'>Redistribuye el saldo pendiente ({$remaining}) en más cuotas, extendiendo el plazo de pago.</p>
                </div>
                <div class='grid grid-cols-2 gap-3'>
                    <div class='rounded-lg bg-success-50 dark:bg-success-950 p-3'>
                        <strong class='text-success-700 dark:text-success-300'>✓ SÍ hace:</strong>
                        <ul class='mt-1 text-xs space-y-1'>
                            <li>• Crea nuevo crédito con más cuotas</li>
                            <li>• Cierra el crédito actual</li>
                            <li>• Preserva historial de pagos</li>
                        </ul>
                    </div>
                    <div class='rounded-lg bg-danger-50 dark:bg-danger-950 p-3'>
                        <strong class='text-danger-700 dark:text-danger-300'>✗ NO hace:</strong>
                        <ul class='mt-1 text-xs space-y-1'>
                            <li>• No entrega dinero nuevo</li>
                            <li>• No condona deuda</li>
                            <li>• No cambia el saldo total</li>
                        </ul>
                    </div>
                </div>
            </div>
        ";
    }

    private function getExtendWithInterestDescription(): string
    {
        $remaining = Format::money($this->record->remaining_balance);
        $rate = $this->record->interest_rate;

        return "
            <div class='space-y-3 text-sm'>
                <div class='rounded-lg bg-warning-50 dark:bg-warning-950 p-3'>
                    <strong>📅 ¿Qué hace?</strong>
                    <p class='mt-1'>Extiende el plazo aplicando <strong>intereses adicionales</strong> sobre el saldo pendiente ({$remaining}).</p>
                </div>
                <div class='grid grid-cols-2 gap-3'>
                    <div class='rounded-lg bg-success-50 dark:bg-success-950 p-3'>
                        <strong class='text-success-700 dark:text-success-300'>✓ SÍ hace:</strong>
                        <ul class='mt-1 text-xs space-y-1'>
                            <li>• Suma intereses al saldo (tasa actual: {$rate}%)</li>
                            <li>• Crea nuevo crédito con nuevo total</li>
                            <li>• Preserva pagos anteriores</li>
                        </ul>
                    </div>
                    <div class='rounded-lg bg-danger-50 dark:bg-danger-950 p-3'>
                        <strong class='text-danger-700 dark:text-danger-300'>✗ NO hace:</strong>
                        <ul class='mt-1 text-xs space-y-1'>
                            <li>• No entrega dinero nuevo</li>
                            <li>• No condona deuda</li>
                            <li>• El total a pagar AUMENTA</li>
                        </ul>
                    </div>
                </div>
            </div>
        ";
    }

    private function getRestructureDescription(): string
    {
        $remaining = Format::money($this->record->remaining_balance);

        return "
            <div class='space-y-3 text-sm'>
                <div class='rounded-lg bg-gray-100 dark:bg-gray-800 p-3'>
                    <strong>🔧 ¿Qué hace?</strong>
                    <p class='mt-1'>Renegocia las condiciones del crédito (saldo: {$remaining}). Puede cambiar tasa, plazo o capitalizar intereses vencidos.</p>
                </div>
                <div class='grid grid-cols-2 gap-3'>
                    <div class='rounded-lg bg-success-50 dark:bg-success-950 p-3'>
                        <strong class='text-success-700 dark:text-success-300'>✓ SÍ hace:</strong>
                        <ul class='mt-1 text-xs space-y-1'>
                            <li>• Puede cambiar tasa de interés</li>
                            <li>• Puede cambiar número de cuotas</li>
                            <li>• Puede capitalizar intereses</li>
                        </ul>
                    </div>
                    <div class='rounded-lg bg-danger-50 dark:bg-danger-950 p-3'>
                        <strong class='text-danger-700 dark:text-danger-300'>✗ NO hace:</strong>
                        <ul class='mt-1 text-xs space-y-1'>
                            <li>• No entrega dinero nuevo</li>
                            <li>• No borra pagos previos</li>
                        </ul>
                    </div>
                </div>
                <div class='rounded-lg bg-info-50 dark:bg-info-950 p-2 text-xs'>
                    <strong>💡 Uso típico:</strong> Cliente con dificultades de pago solicita renegociación.
                </div>
            </div>
        ";
    }

    private function getRestructureCustomDescription(): string
    {
        return "
            <div class='space-y-3 text-sm'>
                <div class='rounded-lg bg-purple-50 dark:bg-purple-950 p-3'>
                    <strong>🔧 Reestructuración con Cuotas Personalizadas</strong>
                    <p class='mt-1'>Permite definir manualmente el monto de cada cuota (cuotas irregulares).</p>
                </div>
                <div class='rounded-lg bg-warning-50 dark:bg-warning-950 p-2 text-xs'>
                    <strong>⚠️ Avanzado:</strong> Use esta opción solo si necesita cuotas de diferentes montos.
                </div>
            </div>
        ";
    }

    private function getRefinanceDescription(): string
    {
        $remaining = Format::money($this->record->remaining_balance);

        return "
            <div class='space-y-3 text-sm'>
                <div class='rounded-lg bg-warning-50 dark:bg-warning-950 p-3'>
                    <strong>💰 ¿Qué hace?</strong>
                    <p class='mt-1'>Crea un nuevo crédito sumando el saldo pendiente ({$remaining}) <strong>+ capital adicional</strong>.</p>
                </div>
                <div class='rounded-lg bg-danger-100 dark:bg-danger-900 p-3 border border-danger-300 dark:border-danger-700'>
                    <strong class='text-danger-700 dark:text-danger-300'>⚠️ GENERA DESEMBOLSO</strong>
                    <p class='mt-1 text-xs'>Se entregará el capital adicional al cliente como dinero nuevo.</p>
                </div>
                <div class='grid grid-cols-2 gap-3'>
                    <div class='rounded-lg bg-success-50 dark:bg-success-950 p-3'>
                        <strong class='text-success-700 dark:text-success-300'>✓ SÍ hace:</strong>
                        <ul class='mt-1 text-xs space-y-1'>
                            <li>• Paga el saldo pendiente interno</li>
                            <li>• Entrega dinero adicional</li>
                            <li>• Crea nuevo crédito mayor</li>
                        </ul>
                    </div>
                    <div class='rounded-lg bg-danger-50 dark:bg-danger-950 p-3'>
                        <strong class='text-danger-700 dark:text-danger-300'>✗ NO hace:</strong>
                        <ul class='mt-1 text-xs space-y-1'>
                            <li>• No condona deuda</li>
                            <li>• No reduce el saldo</li>
                        </ul>
                    </div>
                </div>
            </div>
        ";
    }

    private function getRenewDescription(): string
    {
        $remaining = Format::money($this->record->remaining_balance);
        $originalAmount = Format::money($this->record->amount);

        return "
            <div class='space-y-3 text-sm'>
                <div class='rounded-lg bg-success-50 dark:bg-success-950 p-3'>
                    <strong>🔄 ¿Qué hace?</strong>
                    <p class='mt-1'>Crea un nuevo crédito que paga el saldo pendiente ({$remaining}) y entrega la diferencia al cliente.</p>
                </div>
                <div class='rounded-lg bg-warning-100 dark:bg-warning-900 p-3 border border-warning-300 dark:border-warning-700'>
                    <strong class='text-warning-700 dark:text-warning-300'>💵 GENERA DESEMBOLSO</strong>
                    <p class='mt-1 text-xs'>Se entrega: Nuevo monto - Saldo pendiente = Dinero al cliente</p>
                </div>
                <div class='rounded-lg bg-info-50 dark:bg-info-950 p-2 text-xs'>
                    <strong>💡 Uso típico:</strong> Cliente con buen historial de pagos que quiere renovar su crédito.<br>
                    Monto original: {$originalAmount} | Saldo actual: {$remaining}
                </div>
            </div>
        ";
    }

    // ═══════════════════════════════════════════════════════════════════════════════
    // ACTIONS
    // ═══════════════════════════════════════════════════════════════════════════════

    /**
     * Duplicates the credit as a new editable credit.
     * NOTE: This operation DOES generate a new disbursement.
     */
    private function duplicateCredit(): void
    {
        try {
            /** @var Credit $original */
            $original = $this->record;

            $new = app(CreditOperationService::class)->executeOperation(
                $original,
                [
                    'operation_type' => 'clone',
                    'new_installments' => $original->installments_count,
                    'amount' => $original->amount,
                ]
            );

            app(CreditOperationService::class)->audit(
                creditId: $new->id,
                action: CreditAuditEvent::CREDIT_CLONED,
                data: ['from_credit_id' => $original->id]
            );

            Notification::make()
                ->title('Crédito duplicado exitosamente')
                ->success()
                ->body("Se creó el crédito #{$new->code}. Puede editarlo antes de confirmar el desembolso.")
                ->persistent()
                ->actions([
                    // Filament 5 unificó las acciones en Filament\Actions\Action (ya
                    // importado arriba). El FQN antiguo no existe y reventaba al duplicar.
                    Action::make('view')
                        ->label('Ver nuevo crédito')
                        ->url(CreditResource::getUrl('edit', ['record' => $new])),
                ])
                ->send();

            redirect()->to(
                CreditResource::getUrl('edit', ['record' => $new])
            );
        } catch (\Throwable $e) {
            Notification::make()
                ->title('No se pudo duplicar el crédito')
                ->danger()
                ->body($e->getMessage())
                ->send();
        }
    }

    /**
     * Anula un pago directamente desde la tabla de pagos de la vista del crédito.
     *
     * Se dispara con `mountAction('voidPayment', { payment: <id> })` desde el Blade
     * `filament.infolists.components.payments-table`. "Anular" = REVERSAR (asiento
     * compensatorio + `voided=1`), NUNCA un DELETE físico: los pagos son registros
     * contables y borrarlos corrompería el ledger. Reutiliza exactamente el mismo
     * mecanismo que la tabla global `/admin/payments`
     * (PaymentManager::canDeletePayment + deletePayment).
     */
    public function voidPaymentAction(): Action
    {
        return Action::make('voidPayment')
            ->icon('heroicon-o-trash')
            ->color('danger')
            // Solo administradores pueden anular pagos. `visible()` impide montar la
            // acción a supervisores/cobradores; el guard dentro de `action()` es el
            // respaldo definitivo si alguien fuerza el `mountAction`.
            ->visible(fn (): bool => $this->userCanVoidPayments())
            ->modalIcon('heroicon-o-trash')
            ->modalIconColor('danger')
            ->modalHeading('Anular pago')
            ->modalDescription(function (array $arguments): ?string {
                $payment = $this->resolvePaymentArgument($arguments);

                if (! $payment) {
                    return null;
                }

                /** @var Credit $credit */
                $credit = $this->record;

                return 'Anularás un pago de '.Format::money($payment->amount).
                    ' del crédito #'.$credit->code.'. Quedará marcado como ANULADO '.
                    'y se generará un asiento de reversión (no se elimina físicamente).';
            })
            ->schema([
                Textarea::make('reason')
                    ->label('Motivo de la anulación')
                    ->placeholder('Ej.: pago mal registrado, se traslada a otro crédito, etc.')
                    ->required()
                    ->rows(4),
            ])
            ->modalSubmitActionLabel('Anular pago')
            ->action(function (array $arguments, array $data): void {
                // Respaldo de autorización: la anulación es exclusiva de administradores.
                if (! $this->userCanVoidPayments()) {
                    Notification::make()
                        ->title('No autorizado')
                        ->danger()
                        ->body('Solo un administrador puede anular pagos.')
                        ->send();

                    return;
                }

                $payment = $this->resolvePaymentArgument($arguments);

                if (! $payment) {
                    Notification::make()
                        ->title('Pago no encontrado')
                        ->danger()
                        ->body('El pago ya no existe o no pertenece a este crédito.')
                        ->send();

                    return;
                }

                $manager = app(PaymentManager::class);

                if (! $manager->canDeletePayment($payment)) {
                    Notification::make()
                        ->title('No se puede anular el pago')
                        ->danger()
                        ->body('Este pago está bloqueado por reglas del sistema (ya anulado, crédito cerrado o con auditoría crítica).')
                        ->send();

                    return;
                }

                $manager->deletePayment($payment, $data['reason']);

                $this->record->refresh();

                Notification::make()
                    ->title('Pago anulado')
                    ->success()
                    ->body('Se registró la reversión del pago.')
                    ->send();
            });
    }

    /**
     * Resuelve el pago del argumento `payment` EXIGIENDO que pertenezca a este crédito.
     *
     * Consultar vía `$this->record->payments()` (en lugar de `Payment::find`) garantiza
     * el aislamiento multi-tenant: un id de pago ajeno al crédito/compañía inyectado en
     * el argumento del action nunca resuelve a un modelo.
     */
    /**
     * ¿El usuario autenticado puede anular pagos? Restringido a administradores
     * (`admin`) y al superusuario de plataforma (`super_admin`). Supervisores y
     * cobradores NO pueden anular. Mismo criterio de rol que gobierna el panel /admin.
     */
    private function userCanVoidPayments(): bool
    {
        return (bool) auth()->user()?->hasAnyRole(['super_admin', 'admin']);
    }

    private function resolvePaymentArgument(array $arguments): ?Payment
    {
        $paymentId = $arguments['payment'] ?? null;

        if (! $paymentId) {
            return null;
        }

        /** @var Credit $credit */
        $credit = $this->record;

        /** @var Payment|null $payment */
        $payment = $credit->payments()->whereKey($paymentId)->first();

        return $payment;
    }
}
