<?php

namespace App\Filament\Server\Resources;

use App\Filament\Server\Resources\ScheduleResource\Pages;
use App\Filament\Server\Resources\ScheduleResource\RelationManagers\TasksRelationManager;
use App\Helpers\Utilities;
use App\Models\Permission;
use App\Models\Schedule;
use App\Traits\Filament\BlockAccessInConflict;
use App\Traits\Filament\CanCustomizePages;
use App\Traits\Filament\CanCustomizeRelations;
use Carbon\Carbon;
use Exception;
use Filament\Facades\Filament;
use Filament\Forms\Components\Actions;
use Filament\Forms\Components\Actions\Action;
use Filament\Forms\Components\Section;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
use Filament\Forms\Components\ToggleButtons;
use Filament\Forms\Form;
use Filament\Forms\Set;
use Filament\Notifications\Notification;
use Filament\Resources\Resource;
use Filament\Support\Exceptions\Halt;
use Illuminate\Database\Eloquent\Model;

class ScheduleResource extends Resource
{
    use BlockAccessInConflict;
    use CanCustomizePages;
    use CanCustomizeRelations;

    protected static ?string $model = Schedule::class;

    protected static ?int $navigationSort = 4;

    protected static ?string $navigationIcon = 'tabler-clock';

    public static function canViewAny(): bool
    {
        return auth()->user()->can(Permission::ACTION_SCHEDULE_READ, Filament::getTenant());
    }

    public static function canCreate(): bool
    {
        return auth()->user()->can(Permission::ACTION_SCHEDULE_CREATE, Filament::getTenant());
    }

    public static function canEdit(Model $record): bool
    {
        return auth()->user()->can(Permission::ACTION_SCHEDULE_UPDATE, Filament::getTenant());
    }

    public static function canDelete(Model $record): bool
    {
        return auth()->user()->can(Permission::ACTION_SCHEDULE_DELETE, Filament::getTenant());
    }

    public static function form(Form $form): Form
    {
        return $form
            ->columns([
                'default' => 4,
                'lg' => 5,
            ])
            ->schema([
                TextInput::make('name')
                    ->columnSpan([
                        'default' => 4,
                        'md' => 3,
                        'lg' => 4,
                    ])
                    ->label('Schedule Name')
                    ->placeholder('A human readable identifier for this schedule.')
                    ->autocomplete(false)
                    ->required(),
                ToggleButtons::make('Status')
                    ->formatStateUsing(fn (Schedule $schedule) => !$schedule->is_active ? 'inactive' : ($schedule->is_processing ? 'processing' : 'active'))
                    ->options(fn (Schedule $schedule) => !$schedule->is_active ? ['inactive' => 'Inactive'] : ($schedule->is_processing ? ['processing' => 'Processing'] : ['active' => 'Active']))
                    ->colors([
                        'inactive' => 'danger',
                        'processing' => 'warning',
                        'active' => 'success',
                    ])
                    ->visibleOn('view')
                    ->columnSpan([
                        'default' => 4,
                        'md' => 1,
                        'lg' => 1,
                    ]),
                Toggle::make('only_when_online')
                    ->label('Only when Server is Online?')
                    ->hintIconTooltip('Only execute this schedule when the server is in a running state.')
                    ->hintIcon('tabler-question-mark')
                    ->inline(false)
                    ->columnSpan([
                        'default' => 2,
                        'lg' => 3,
                    ])
                    ->required()
                    ->default(1),
                Toggle::make('is_active')
                    ->label('Enable Schedule?')
                    ->hintIconTooltip('This schedule will be executed automatically if enabled.')
                    ->hintIcon('tabler-question-mark')
                    ->inline(false)
                    ->columnSpan([
                        'default' => 2,
                        'lg' => 2,
                    ])
                    ->required()
                    ->default(1),
                TextInput::make('cron_minute')
                    ->columnSpan([
                        'default' => 2,
                        'lg' => 1,
                    ])
                    ->label('Minute')
                    ->default('*/5')
                    ->required(),
                TextInput::make('cron_hour')
                    ->columnSpan([
                        'default' => 2,
                        'lg' => 1,
                    ])
                    ->label('Hour')
                    ->default('*')
                    ->required(),
                TextInput::make('cron_day_of_month')
                    ->columnSpan([
                        'default' => 2,
                        'lg' => 1,
                    ])
                    ->label('Day of Month')
                    ->default('*')
                    ->required(),
                TextInput::make('cron_month')
                    ->columnSpan([
                        'default' => 2,
                        'lg' => 1,
                    ])
                    ->label('Month')
                    ->default('*')
                    ->required(),
                TextInput::make('cron_day_of_week')
                    ->columnSpan([
                        'default' => 2,
                        'lg' => 1,
                    ])
                    ->label('Day of Week')
                    ->default('*')
                    ->required(),
                Section::make('Presets')
                    ->hiddenOn('view')
                    ->columns(1)
                    ->schema([
                        Actions::make([
                            Action::make('hourly')
                                ->disabled(fn (string $operation) => $operation === 'view')
                                ->action(function (Set $set) {
                                    $set('cron_minute', '0');
                                    $set('cron_hour', '*');
                                    $set('cron_day_of_month', '*');
                                    $set('cron_month', '*');
                                    $set('cron_day_of_week', '*');
                                }),
                            Action::make('daily')
                                ->disabled(fn (string $operation) => $operation === 'view')
                                ->action(function (Set $set) {
                                    $set('cron_minute', '0');
                                    $set('cron_hour', '0');
                                    $set('cron_day_of_month', '*');
                                    $set('cron_month', '*');
                                    $set('cron_day_of_week', '*');
                                }),
                            Action::make('weekly')
                                ->disabled(fn (string $operation) => $operation === 'view')
                                ->action(function (Set $set) {
                                    $set('cron_minute', '0');
                                    $set('cron_hour', '0');
                                    $set('cron_day_of_month', '*');
                                    $set('cron_month', '*');
                                    $set('cron_day_of_week', '0');
                                }),
                            Action::make('monthly')
                                ->disabled(fn (string $operation) => $operation === 'view')
                                ->action(function (Set $set) {
                                    $set('cron_minute', '0');
                                    $set('cron_hour', '0');
                                    $set('cron_day_of_month', '1');
                                    $set('cron_month', '*');
                                    $set('cron_day_of_week', '0');
                                }),
                            Action::make('every_x_minutes')
                                ->disabled(fn (string $operation) => $operation === 'view')
                                ->form([
                                    TextInput::make('x')
                                        ->label('')
                                        ->numeric()
                                        ->minValue(1)
                                        ->maxValue(60)
                                        ->prefix('Every')
                                        ->suffix('Minutes'),
                                ])
                                ->action(function (Set $set, $data) {
                                    $set('cron_minute', '*/' . $data['x']);
                                    $set('cron_hour', '*');
                                    $set('cron_day_of_month', '*');
                                    $set('cron_month', '*');
                                    $set('cron_day_of_week', '*');
                                }),
                            Action::make('every_x_hours')
                                ->disabled(fn (string $operation) => $operation === 'view')
                                ->form([
                                    TextInput::make('x')
                                        ->label('')
                                        ->numeric()
                                        ->minValue(1)
                                        ->maxValue(24)
                                        ->prefix('Every')
                                        ->suffix('Hours'),
                                ])
                                ->action(function (Set $set, $data) {
                                    $set('cron_minute', '0');
                                    $set('cron_hour', '*/' . $data['x']);
                                    $set('cron_day_of_month', '*');
                                    $set('cron_month', '*');
                                    $set('cron_day_of_week', '*');
                                }),
                            Action::make('every_x_days')
                                ->disabled(fn (string $operation) => $operation === 'view')
                                ->form([
                                    TextInput::make('x')
                                        ->label('')
                                        ->numeric()
                                        ->minValue(1)
                                        ->maxValue(24)
                                        ->prefix('Every')
                                        ->suffix('Days'),
                                ])
                                ->action(function (Set $set, $data) {
                                    $set('cron_minute', '0');
                                    $set('cron_hour', '0');
                                    $set('cron_day_of_month', '*/' . $data['x']);
                                    $set('cron_month', '*');
                                    $set('cron_day_of_week', '*');
                                }),
                            Action::make('every_x_months')
                                ->disabled(fn (string $operation) => $operation === 'view')
                                ->form([
                                    TextInput::make('x')
                                        ->label('')
                                        ->numeric()
                                        ->minValue(1)
                                        ->maxValue(24)
                                        ->prefix('Every')
                                        ->suffix('Months'),
                                ])
                                ->action(function (Set $set, $data) {
                                    $set('cron_minute', '0');
                                    $set('cron_hour', '0');
                                    $set('cron_day_of_month', '1');
                                    $set('cron_month', '*/' . $data['x']);
                                    $set('cron_day_of_week', '*');
                                }),
                            Action::make('every_x_day_of_week')
                                ->disabled(fn (string $operation) => $operation === 'view')
                                ->form([
                                    Select::make('x')
                                        ->label('')
                                        ->prefix('Every')
                                        ->options([
                                            '1' => 'Monday',
                                            '2' => 'Tuesday',
                                            '3' => 'Wednesday',
                                            '4' => 'Thursday',
                                            '5' => 'Friday',
                                            '6' => 'Saturday',
                                            '0' => 'Sunday',
                                        ])
                                        ->selectablePlaceholder(false)
                                        ->native(false),
                                ])
                                ->action(function (Set $set, $data) {
                                    $set('cron_minute', '0');
                                    $set('cron_hour', '0');
                                    $set('cron_day_of_month', '*');
                                    $set('cron_month', '*');
                                    $set('cron_day_of_week', $data['x']);
                                }),
                        ]),
                    ]),
            ]);
    }

    public static function getDefaultRelations(): array
    {
        return [
            TasksRelationManager::class,
        ];
    }

    public static function getDefaultPages(): array
    {
        return [
            'index' => Pages\ListSchedules::route('/'),
            'create' => Pages\CreateSchedule::route('/create'),
            'view' => Pages\ViewSchedule::route('/{record}'),
            'edit' => Pages\EditSchedule::route('/{record}/edit'),
        ];
    }

    public static function getNextRun(string $minute, string $hour, string $dayOfMonth, string $month, string $dayOfWeek): Carbon
    {
        try {
            return Utilities::getScheduleNextRunDate($minute, $hour, $dayOfMonth, $month, $dayOfWeek);
        } catch (Exception) {
            Notification::make()
                ->title('The cron data provided does not evaluate to a valid expression')
                ->danger()
                ->send();

            throw new Halt();
        }
    }
}
