<?php

namespace Modules\Payment\Filament\Actions;

use Filament\Actions\Action;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
use Filament\Notifications\Notification;
use Filament\Schemas\Components\Utilities\Get;
use Filament\Support\Icons\Heroicon;
use Modules\Booking\Enums\BookingStatus;
use Modules\Booking\Models\Booking;
use Modules\Payment\Actions\RefundBookingAction;
use Modules\Payment\Exceptions\RefundFailedException;
use Modules\Payment\Exceptions\RefundNotAllowedException;

/**
 * Shared between BookingsTable (row action) and ViewBooking (header action)
 * in the Booking module — lets staff refund a booking directly instead of
 * hunting up its Payment on the Refunds resource (ProcessRefundAction). Both
 * surfaces call the same RefundBookingAction used by the API.
 */
class RefundBookingTableAction
{
    public static function make(): Action
    {
        return Action::make('refund')
            ->label('Refund')
            ->icon(Heroicon::OutlinedReceiptRefund)
            ->color('danger')
            ->visible(fn (): bool => auth()->user()?->can('process_refunds') ?? false)
            ->disabled(fn (Booking $record): bool => $record->status !== BookingStatus::Confirmed)
            ->schema([
                Toggle::make('full_refund')
                    ->label('Full refund')
                    ->live()
                    ->default(true)
                    ->helperText(fn (Booking $record): string => 'Refundable balance: '.(app(RefundBookingAction::class)
                        ->resolveRefundablePayment($record)?->refundableBalance() ?? '0.00')),
                TextInput::make('amount')
                    ->numeric()
                    ->minValue(0.01)
                    ->visible(fn (Get $get): bool => ! $get('full_refund'))
                    ->required(fn (Get $get): bool => ! $get('full_refund'))
                    ->maxValue(fn (Booking $record): ?string => app(RefundBookingAction::class)
                        ->resolveRefundablePayment($record)?->refundableBalance()),
                Textarea::make('reason')
                    ->required(),
            ])
            ->action(function (Booking $record, array $data): void {
                $amount = $data['full_refund']
                    ? app(RefundBookingAction::class)->resolveRefundablePayment($record)?->refundableBalance() ?? '0.00'
                    : (string) $data['amount'];

                try {
                    app(RefundBookingAction::class)->handle(
                        $record,
                        $amount,
                        $data['reason'],
                        auth()->id(),
                    );

                    // RefundBookingAction only dispatches RefundProcessed;
                    // the actual Confirmed → Cancelled flip happens in
                    // MarkBookingRefunded, which is queued (kept that way —
                    // it's the API/webhook path's source of truth too). Set
                    // it here as well, scoped to just this Filament action,
                    // so the row/page reflects it the moment this request
                    // finishes instead of after a queue worker catches up.
                    if ($record->status === BookingStatus::Confirmed) {
                        $record->update(['status' => BookingStatus::Cancelled]);
                    }

                    Notification::make()
                        ->title('Refund processed')
                        ->success()
                        ->send();
                } catch (RefundNotAllowedException|RefundFailedException $exception) {
                    Notification::make()
                        ->title('Refund failed')
                        ->body($exception->getMessage())
                        ->danger()
                        ->send();
                }
            });
    }
}
