<?php

namespace App\Filament\Resources;

use App\Filament\Resources\BookingResource\Pages;
use App\Models\Booking;
use App\Models\BookingStatus;
use App\Models\Computer;
use App\Models\Enum\BookingStatusEnum;
use App\Models\Enum\ComputerStatusEnum;
use App\Models\Enum\RoleEnum;
use App\Models\Side;
use App\Models\User;
use Filament\Forms;
use Filament\Forms\Form;
use Filament\Resources\Resource;
use Filament\Support\Enums\ActionSize;
use Filament\Tables;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\SoftDeletingScope;
use Illuminate\Validation\ValidationException;
use Carbon\Carbon;

class BookingResource extends Resource
{
    protected static ?string $model = Booking::class;

    protected static ?string $navigationGroup = "Booking Management";

    protected static ?string $navigationIcon = 'heroicon-o-bookmark';

    public static function canViewAny(): bool
    {
        return auth()->user()->role_id !== RoleEnum::ID[RoleEnum::SIDE_USER];
    }

    public static function form(Form $form): Form
    {
        return $form
            ->schema([
                Forms\Components\Select::make('user_id')
                    ->label('User')
                    ->searchable()
                    ->options(function (callable $get) {
                        return User::query()
                            ->where('name', 'like', '%' . $get('search') . '%')
                            ->pluck('name', 'id');
                    })
                    ->required()
                    ->placeholder('Select User'),

                Forms\Components\Select::make('side_id')
                    ->label('Side (Library)')
                    ->searchable()
                    ->options(function (callable $get) {
                        return Side::query()
                            ->where('name', 'like', '%' . $get('search') . '%')
                            ->pluck('name', 'id');
                    })
                    ->required()
                    ->reactive()
                    ->placeholder('Select Side'),

                Forms\Components\DateTimePicker::make('start_time')
                    ->label('Start Time')
                    ->required()
                    ->placeholder('Select Start Time')
                    ->reactive()
                    ->rules([
                        fn (Forms\Get $get) => function (string $attribute, $value, $fail) use ($get) {
                            if ($value && now()->gt(Carbon::parse($value))) {
                                $fail('La fecha y hora de inicio no puede ser una fecha pasada.');
                            }
                        },
                    ])
                    ->afterStateUpdated(function (callable $set, callable $get, $state) {
                        if ($state && now()->gt(Carbon::parse($state))) {
                            $set('start_time', null);
                            \Filament\Notifications\Notification::make()
                                ->title('Error en la fecha de inicio')
                                ->body('La fecha y hora de inicio no puede ser una fecha pasada.')
                                ->danger()
                                ->send();
                        }
                    }),

                Forms\Components\DateTimePicker::make('end_time')
                    ->label('End Time')
                    ->required()
                    ->placeholder('Select End Time')
                    ->reactive()
                    ->rules([
                        fn (Forms\Get $get) => function (string $attribute, $value, $fail) use ($get) {
                            $startTime = $get('start_time');

                            if ($value && $startTime && Carbon::parse($value)->lt(Carbon::parse($startTime))) {
                                $fail('La hora de finalización debe ser posterior a la hora de inicio.');
                            }
                        },
                    ])
                    ->afterStateUpdated(function (callable $set, callable $get, $state) {
                        $start = $get('start_time');

                        if ($start && $state) {
                            $diffInMinutes = now()->parse($state)->diffInMinutes(now()->parse($start));
                            if ($diffInMinutes > -1) {
                                $set('end_time', null);
                                \Filament\Notifications\Notification::make()
                                    ->title('Duración de la reserva no válida')
                                    ->body('La duración mínima de la reserva debe ser de al menos 1 minuto.')
                                    ->danger()
                                    ->send();
                            }
                        }
                    }),

                Forms\Components\Select::make('computer_id')
                    ->label('Assigned Computer')
                    ->searchable()
                    ->required()
                    ->reactive()
                    ->placeholder('Select Computer')
                    ->options(function (callable $get) {
                        if ($get("id")) {
                            $computer = Computer::find($get('computer_id'));

                            return [$computer->id => $computer->name];
                        }

                        $sideId = $get('side_id');
                        $startTime = $get('start_time');
                        $endTime = $get('end_time');

                        if (!$sideId || !$startTime || !$endTime) {
                            return [];
                        }

                        $computers = Computer::query()
                            ->where('side_id', $sideId)
                            ->where('reserved', false)
                            ->where('computer_status_id', ComputerStatusEnum::ID[ComputerStatusEnum::ACTIVE])
                            ->whereDoesntHave('bookings', function ($query) use ($startTime, $endTime) {
                                $startTime = Carbon::parse($startTime)->toDateTimeString();
                                $endTime = Carbon::parse($endTime)->toDateTimeString();

                                $query->where(function ($query) use ($startTime, $endTime) {
                                    $query->whereBetween('start_time', [$startTime, $endTime])
                                        ->orWhereBetween('end_time', [$startTime, $endTime])
                                        ->orWhere(function ($query) use ($startTime, $endTime) {
                                            $query->where('start_time', '<=', $startTime)
                                                ->where('end_time', '>=', $endTime);
                                        });
                                });
                            })
                            ->pluck('name', 'id');

                        if ($computers->isEmpty()) {
                            \Filament\Notifications\Notification::make()
                                ->title('Creation Booking Error')
                                ->body('No computers available.')
                                ->danger()
                                ->send()
                            ;

                            return [];
                        }

                        return $computers;
                    })
                    ->disabled(fn (callable $get) => $get('id') !== null),

                Forms\Components\Select::make('booking_status_id')
                    ->label('Status')
                    ->searchable()
                    ->options(function (callable $get) {
                        return BookingStatus::query()
                            ->where('name', 'like', '%' . $get('search') . '%')
                            ->pluck('name', 'id');
                    })
                    ->default(BookingStatusEnum::ID[BookingStatusEnum::PENDING])
                    ->required(),
            ]);
    }

    public static function table(Table $table): Table
    {
        return $table
            ->columns([
                Tables\Columns\TextColumn::make("user.name")
                    ->label("Booked By")
                    ->searchable()
                    ->sortable(),

                Tables\Columns\TextColumn::make("computer.name")
                    ->label("Computer")
                    ->searchable()
                    ->sortable(),

                Tables\Columns\TextColumn::make("side.name")
                    ->label("Side (Library)")
                    ->searchable()
                    ->sortable(),

                Tables\Columns\TextColumn::make("bookingStatus.name")
                    ->label("Status")
                    ->searchable()
                    ->sortable()
                    ->formatStateUsing(function ($state) {
                        if (is_null($state)) {
                            return '<span class="text-gray-500">No Status</span>';
                        }

                        switch ($state) {
                            case 'Pendiente':
                                return '<span class="text-yellow-500">⏰ Pending</span>';
                            case 'Confirmada':
                                return '<span class="text-green-500">✅ Confirmed</span>';
                            case 'Caducada':
                                return '<span class="text-orange-500">⚠️ Expired</span>';
                            case 'Cancelada':
                                return '<span class="text-red-500">❌ Cancelled</span>';
                            default:
                                return '<span class="text-gray-500">Unknown</span>';
                        }
                    })
                    ->html(),

                Tables\Columns\TextColumn::make("start_time")
                    ->label('Start Time')
                    ->formatStateUsing(function ($record) {
                        return \Carbon\Carbon::parse($record->start_time)->format('d/m/Y H:i:s');
                    })
                    ->searchable()
                    ->sortable(),

                Tables\Columns\TextColumn::make("end_time")
                    ->label('End Time')
                    ->formatStateUsing(function ($record) {
                        return \Carbon\Carbon::parse($record->end_time)->format('d/m/Y H:i:s');
                    })
                    ->searchable()
                    ->sortable(),
            ])
            ->filters([
                Tables\Filters\SelectFilter::make('booking_status_id')
                    ->label('Booking Status')
                    ->options(BookingStatus::all()->pluck('name', 'id')->toArray())
                    ->searchable()
                    ->placeholder('Select Booking Status'),

                Tables\Filters\SelectFilter::make('side_id')
                    ->label('Select Side (Library)')
                    ->options(Side::all()->pluck('name', 'id')->toArray())
                    ->searchable()
                    ->placeholder('Select Side (Library)'),

                Tables\Filters\SelectFilter::make('user_id')
                    ->label('Reserved By')
                    ->options(User::all()->pluck('name', 'id')->toArray())
                    ->searchable()
                    ->placeholder('Reserved by'),

                Tables\Filters\SelectFilter::make('computer_id')
                    ->label('Computer')
                    ->options(Computer::all()->pluck('name', 'id')->toArray())
                    ->searchable()
                    ->placeholder('Select Computer'),

                Tables\Filters\Filter::make('booking_times')
                    ->label('Booking Times')
                    ->form([
                        Forms\Components\DateTimePicker::make("start_time"),
                        Forms\Components\DateTimePicker::make("end_time"),
                    ])
                    ->query(function (Builder $query, array $data): Builder {
                    return $query
                        ->when(
                            $data['start_time'] ?? null,
                            fn (Builder $query, $startTime) => $query->where('start_time', '>=', \Carbon\Carbon::parse($startTime))
                        )
                        ->when(
                            $data['end_time'] ?? null,
                            fn (Builder $query, $endTime) => $query->where('end_time', '<=', \Carbon\Carbon::parse($endTime))
                        );
                }),

                Tables\Filters\Filter::make('id')
                    ->form([
                        Forms\Components\TextInput::make("id")
                            ->visible(false),
                    ])
                    ->query(function (Builder $query, array $data) {
                        if ($data["id"]) {
                            return $query->where('id', $data["id"]);
                        }
                        return $query;
                    }),
            ])
            ->actions([
                Tables\Actions\ActionGroup::make([
                    Tables\Actions\EditAction::make(),

                    Tables\Actions\Action::make('cancel_reservation')
                        ->label('Cancel Booking')
                        ->icon('heroicon-o-x-circle')
                        ->color('danger')
                        ->action(function ($record) {
                            $cancelledBookingId = BookingStatus::where("name", "Cancelada")->first()?->id;

                            $record->update(['booking_status_id' => $cancelledBookingId]);

                            $record->userSession->delete();

                            session()->flash('success', 'Booking has been cancelled.');
                        })
                        ->requiresConfirmation()
                        ->modalHeading('Are you sure you want to cancel this booking?')
                        ->modalSubmitActionLabel('Confirm')
                        ->modalDescription('Once cancelled, booking does not restored.')
                        ->visible(function ($record) {
                            return $record->booking_status_id !== BookingStatusEnum::ID[BookingStatusEnum::CANCELLED];
                        }),

                    Tables\Actions\Action::make('go_to_terminal')
                        ->label('Go To Terminal')
                        ->color("info")
                        ->url(fn ($record) => "/admin/computers?tableSearch=". $record->computer->name)
                        ->icon('heroicon-o-computer-desktop'),

                    Tables\Actions\Action::make('go_to_session')
                        ->label('Go To User Session')
                        ->color("warning")
                        ->url(fn ($record) => "/admin/user-sessions?tableFilters[id][id]=". $record->userSession?->id)
                        ->icon('heroicon-o-clock')
                        ->disabled(fn ($record) => !isset($record->userSession)),
                ])
                ->label('Actions')
                ->icon('heroicon-m-ellipsis-vertical')
                ->size(ActionSize::Small)
                ->color('primary')
                ->button(),
            ])
            ->bulkActions([
                Tables\Actions\BulkActionGroup::make([
                    Tables\Actions\DeleteBulkAction::make(),
                ]),
            ]);
    }

    public static function getRelations(): array
    {
        return [
            //
        ];
    }

    public static function getPages(): array
    {
        return [
            'index' => Pages\ListBookings::route('/'),
            'create' => Pages\CreateBooking::route('/create'),
            'edit' => Pages\EditBooking::route('/{record}/edit'),
        ];
    }
}
