<?php

namespace App\Filament\Resources\Credits\Pages;

use App\Filament\Resources\Credits\CreditResource;
use App\Models\Credit;
use App\Services\CreditCascadeDeletionService;
use Filament\Actions\CreateAction;
use Filament\Actions\DeleteBulkAction;
use Filament\Notifications\Notification;
use Filament\Resources\Pages\ListRecords;
use Filament\Schemas\Components\Tabs\Tab;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Gate;

class ListCredits extends ListRecords
{
    protected static string $resource = CreditResource::class;

    protected function getHeaderActions(): array
    {
        return [
            CreateAction::make(),
        ];
    }

    /**
     * Pestañas del listado.
     *
     * Reemplazan al filtro ternario "Archivados", que definía "archivado" como
     * `status IN ('paid','canceled')` — 2 de los 12 estados reales. Con esa
     * definición, la vista por defecto dejaba pasar los créditos extendidos,
     * refinanciados, renovados y reestructurados: registros históricos que ya
     * fueron sustituidos por un hijo. El mismo préstamo aparecía dos veces.
     *
     * "En curso" usa exactamente la misma regla que la PWA
     * (`Credit::scopeActiveLeafCredits`), así que el listado del panel y la
     * cartera del cobrador no pueden volver a desalinearse.
     */
    public function getTabs(): array
    {
        return [
            'en_curso' => Tab::make('En curso')
                ->icon('heroicon-m-bolt')
                ->modifyQueryUsing($this->enCurso(...))
                ->badge($this->countTab($this->enCurso(...)))
                ->badgeColor('success')
                ->badgeTooltip('Activos, en mora o vencidos que no han sido sustituidos'),

            'reemplazados' => Tab::make('Reemplazados')
                ->icon('heroicon-m-arrow-path')
                ->modifyQueryUsing($this->reemplazados(...))
                ->badge($this->countTab($this->reemplazados(...)))
                ->badgeColor('warning')
                ->badgeTooltip('Extendidos, refinanciados, renovados o reestructurados: existe un crédito hijo que los sustituye'),

            'cerrados' => Tab::make('Cerrados')
                ->icon('heroicon-m-check-circle')
                ->modifyQueryUsing($this->cerrados(...))
                ->badge($this->countTab($this->cerrados(...)))
                ->badgeColor('gray')
                ->badgeTooltip('Pagados, cancelados o en castigo, sin crédito que los sustituya'),

            // Pestaña propia y no mezclados entre los cerrados: un archivado es
            // una decisión pendiente, no un crédito terminado, y hay que poder
            // encontrarlo para retomarlo. Sin esto quedarían perdidos entre
            // cientos de pagados.
            'archivados' => Tab::make('Archivados')
                ->icon('heroicon-m-archive-box')
                ->modifyQueryUsing($this->archivados(...))
                ->badge($this->countTab($this->archivados(...)))
                ->badgeColor('gray')
                ->badgeTooltip('Retirados de la cartera sin decidir aún si se cierran'),

            'todos' => Tab::make('Todos')
                ->icon('heroicon-m-queue-list')
                ->badge($this->countTab($this->todos(...)))
                ->badgeColor('gray'),
        ];
    }

    public function getDefaultActiveTab(): string|int|null
    {
        return 'en_curso';
    }

    /**
     * Las cubetas son métodos con nombre (y no closures inline) para que el
     * genérico `Builder<Credit>` quede declarado: sin él, PHPStan no resuelve
     * los scopes de Eloquent y marca `activeLeafCredits()` como inexistente.
     *
     * @param  Builder<Credit>  $query
     * @return Builder<Credit>
     */
    private function enCurso(Builder $query): Builder
    {
        return $query->activeLeafCredits();
    }

    /**
     * @param  Builder<Credit>  $query
     * @return Builder<Credit>
     */
    private function reemplazados(Builder $query): Builder
    {
        return $query->replacedCredits();
    }

    /**
     * @param  Builder<Credit>  $query
     * @return Builder<Credit>
     */
    private function cerrados(Builder $query): Builder
    {
        // Los archivados tienen pestaña propia, así que se sacan de aquí: si no,
        // aparecerían dos veces y las cubetas dejarían de partir el universo.
        return $query->closedLeafCredits()
            ->where('status', '!=', Credit::STATUS_ARCHIVED);
    }

    /**
     * @param  Builder<Credit>  $query
     * @return Builder<Credit>
     */
    private function archivados(Builder $query): Builder
    {
        return $query->leafCredits()
            ->where('status', Credit::STATUS_ARCHIVED);
    }

    /**
     * @param  Builder<Credit>  $query
     * @return Builder<Credit>
     */
    private function todos(Builder $query): Builder
    {
        return $query;
    }

    /**
     * Cuenta sobre la query del recurso, para que el contador respete el
     * alcance por empresa y rol (un cobrador solo cuenta lo suyo).
     *
     * @param  callable(Builder<Credit>): Builder<Credit>  $scope
     */
    private function countTab(callable $scope): int
    {
        /** @var Builder<Credit> $query */
        $query = static::getResource()::getEloquentQuery();

        return $scope($query)->count();
    }

    protected function getBulkActions(): array
    {
        return [
            DeleteBulkAction::make()
                ->label('Eliminar créditos seleccionados')
                ->requiresConfirmation()
                ->modalHeading('Eliminar créditos')
                ->modalDescription('Se eliminarán solo los créditos permitidos por la política.')
                ->action(function (array $records) {
                    $service = app(CreditCascadeDeletionService::class);

                    $deleted = 0;
                    $skipped = 0;

                    foreach ($records as $credit) {
                        if (! Gate::allows('delete', $credit)) {
                            $skipped++;

                            continue;
                        }

                        $service->deleteCreditCompletely($credit);
                        $deleted++;
                    }

                    Notification::make()
                        ->title('Eliminación completada')
                        ->success()
                        ->body("Eliminados: {$deleted} · Omitidos: {$skipped}")
                        ->send();
                }),
        ];
    }
}
