<?php

declare(strict_types=1);

namespace App\Filament\Resources\Credits\Actions;

use App\DTOs\RenewCreditData;
use App\Enums\Periodicity;
use App\Exceptions\CreditOperationException;
use App\Filament\Resources\Credits\CreditResource;
use App\Models\Credit;
use App\Services\CreditManager;
use App\Services\Credits\CreditOperationOrchestrator;
use App\ValueObjects\Money;
use Carbon\Carbon;
use Filament\Actions\Action;
use Filament\Forms\Components\DatePicker;
use Filament\Forms\Components\Placeholder;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Notifications\Notification;
use Filament\Schemas\Components\Section;

/**
 * Acción de Filament para RENOVAR un crédito.
 *
 * Renovación: Nuevo crédito que paga el anterior y entrega la diferencia al cliente.
 * REQUIERE que el cliente haya realizado pagos previos.
 *
 * NOTA: Como hay desembolso de la diferencia, la fecha de inicio típicamente
 * es "hoy", pero se permite flexibilidad.
 */
class RenewCreditAction
{
    public static function make(): Action
    {
        return Action::make('renew_credit')
            ->label('Renovar')
            ->icon('heroicon-o-arrow-path-rounded-square')
            ->color('success')
            ->modalHeading('Renovar Crédito')
            ->modalDescription(fn (Credit $record): string => sprintf(
                'Crea un nuevo crédito que paga el saldo pendiente (%s) y entrega la diferencia al cliente.',
                '$ '.number_format(app(CreditManager::class)->getRemainingBalance($record), 2, ',', '.')
            )
            )
            ->modalSubmitActionLabel('Renovar Crédito')
            ->modalWidth('lg')
            ->visible(fn (?Credit $record): bool => filled($record) && app(CreditOperationOrchestrator::class)->canRenew($record)
            )
            ->schema(fn (Credit $record): array => [
                Section::make('Información del crédito actual')
                    ->schema([
                        Placeholder::make('original_amount')
                            ->label('Monto original')
                            ->content(fn () => '$ '.number_format($record->amount, 2, ',', '.')),
                        Placeholder::make('current_balance')
                            ->label('Saldo pendiente')
                            ->content(fn () => '$ '.number_format(
                                app(CreditManager::class)->getRemainingBalance($record),
                                2, ',', '.'
                            )),
                        Placeholder::make('total_paid')
                            ->label('Total pagado')
                            ->content(fn () => '$ '.number_format(
                                app(CreditManager::class)->getTotalPaid($record),
                                2, ',', '.'
                            )),
                    ])
                    ->columns(3),

                Section::make('Monto del Nuevo Crédito')
                    ->description('El nuevo monto debe ser igual o mayor al saldo pendiente. La diferencia se entrega al cliente.')
                    ->schema([
                        TextInput::make('new_credit_amount')
                            ->label('Monto del nuevo crédito')
                            ->numeric()
                            ->minValue(fn () => app(CreditManager::class)->getRemainingBalance($record))
                            ->default($record->amount)
                            ->prefix('$')
                            ->required()
                            ->live(onBlur: true)
                            ->helperText('Debe ser >= saldo pendiente'),

                        Placeholder::make('cash_to_client')
                            ->label('Dinero a entregar al cliente')
                            ->content(function ($get) use ($record) {
                                $balance = app(CreditManager::class)->getRemainingBalance($record);
                                $newAmount = (float) ($get('new_credit_amount') ?? 0);
                                $cash = max(0, $newAmount - $balance);

                                return '$ '.number_format($cash, 2, ',', '.');
                            })
                            ->helperText('Este monto se DESEMBOLSARÁ'),
                    ])
                    ->columns(2),

                Section::make('Configuración del nuevo crédito')
                    ->schema([
                        TextInput::make('new_installments')
                            ->label('Número de nuevas cuotas')
                            ->numeric()
                            ->minValue(1)
                            ->maxValue(365)
                            ->default($record->installments_count)
                            ->required(),

                        TextInput::make('new_interest_rate')
                            ->label('Nueva tasa de interés (%)')
                            ->numeric()
                            ->minValue(0)
                            ->maxValue(100)
                            ->step(0.01)
                            ->default($record->interest_rate)
                            ->suffix('%'),

                        Select::make('periodicity')
                            ->label('Periodicidad')
                            ->options(Periodicity::class)
                            ->default($record->periodicity),

                        DatePicker::make('start_date')
                            ->label('Fecha de inicio (desembolso)')
                            ->default(Carbon::today())
                            ->required()
                            ->helperText('Fecha en que se realiza la renovación'),

                        DatePicker::make('first_due_date_override')
                            ->label('Primera fecha de vencimiento (opcional)')
                            ->helperText('Dejar vacío para calcular automáticamente'),
                    ])
                    ->columns(2),

                Textarea::make('reason')
                    ->label('Motivo de la renovación')
                    ->placeholder('Ej: Cliente con buen historial de pagos solicita renovación para nuevo proyecto...')
                    ->rows(2)
                    ->columnSpanFull(),
            ])
            ->action(function (array $data, Credit $record) {
                try {
                    $dto = new RenewCreditData(
                        creditId: $record->id,
                        newInstallmentsCount: (int) $data['new_installments'],
                        newCreditAmount: Money::of((float) $data['new_credit_amount']),
                        newInterestRate: isset($data['new_interest_rate'])
                            ? (string) $data['new_interest_rate']
                            : null,
                        periodicity: isset($data['periodicity']) && $data['periodicity'] instanceof Periodicity
                            ? $data['periodicity']->value
                            : ($data['periodicity'] ?? null),
                        startDate: isset($data['start_date']) ? Carbon::parse($data['start_date']) : null,
                        firstDueDateOverride: isset($data['first_due_date_override'])
                            ? Carbon::parse($data['first_due_date_override'])
                            : null,
                        reason: $data['reason'] ?? null,
                    );

                    $result = app(CreditOperationOrchestrator::class)->renew($dto);

                    Notification::make()
                        ->title('Crédito renovado exitosamente')
                        ->success()
                        ->body($result->getSummaryMessage())
                        ->persistent()
                        ->send();

                    return redirect()->to(
                        CreditResource::getUrl('view', ['record' => $result->newCredit])
                    );
                } catch (CreditOperationException $e) {
                    Notification::make()
                        ->title('No se pudo renovar el crédito')
                        ->danger()
                        ->body($e->getMessage())
                        ->persistent()
                        ->send();
                } catch (\Throwable $e) {
                    Notification::make()
                        ->title('Error inesperado')
                        ->danger()
                        ->body('Ocurrió un error al procesar la operación. Contacte al administrador.')
                        ->persistent()
                        ->send();

                    report($e);
                }
            });
    }
}
