<?php

namespace App\Filament\Resources\Users;

use App\Enums\UserRole;
use App\Filament\Resources\Users\Pages\ManageUsers;
use App\Models\User;
use Filament\Actions\Action;
use Filament\Actions\DeleteAction;
use Filament\Actions\EditAction;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Support\Icons\Heroicon;
use Filament\Tables\Columns\IconColumn;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
use Illuminate\Support\Facades\Gate;
use Illuminate\Validation\ValidationException;

class UserResource extends Resource
{
    protected static ?string $model = User::class;

    protected static string|\UnitEnum|null $navigationGroup = 'Administração';

    protected static ?int $navigationSort = 1;

    protected static string|\BackedEnum|null $navigationIcon = Heroicon::OutlinedUsers;

    protected static ?string $recordTitleAttribute = 'name';

    public static function form(Schema $schema): Schema
    {
        return $schema
            ->components([
                TextInput::make('name')
                    ->label('Nome')
                    ->required()
                    ->maxLength(255),
                TextInput::make('email')
                    ->label('E-mail')
                    ->email()
                    ->required()
                    ->unique(ignoreRecord: true)
                    ->maxLength(255),
                Select::make('role')
                    ->label('Perfil')
                    ->options(self::roleOptions())
                    ->required()
                    ->disabled(fn (?User $record): bool => $record?->isLastActiveAdmin() ?? false),
                Toggle::make('is_active')
                    ->label('Usuário ativo')
                    ->default(true)
                    ->disabled(fn (?User $record): bool => $record?->is(auth()->user()) || ($record?->isLastActiveAdmin() ?? false)),
                TextInput::make('password')
                    ->label('Senha')
                    ->password()
                    ->revealable()
                    ->required(fn (string $operation): bool => $operation === 'create')
                    ->dehydrated(fn (?string $state): bool => filled($state))
                    ->minLength(12)
                    ->maxLength(255),
            ]);
    }

    public static function table(Table $table): Table
    {
        return $table
            ->columns([
                TextColumn::make('name')->label('Nome')->searchable()->sortable(),
                TextColumn::make('email')->label('E-mail')->searchable()->sortable(),
                TextColumn::make('role')
                    ->label('Perfil')
                    ->formatStateUsing(fn (UserRole $state): string => $state->label())
                    ->badge(),
                IconColumn::make('is_active')->label('Ativo')->boolean()->sortable(),
                TextColumn::make('created_at')->label('Criado em')->dateTime('d/m/Y H:i')->sortable(),
            ])
            ->defaultSort('name')
            ->recordActions([
                Action::make('toggleActive')
                    ->label(fn (User $record): string => $record->is_active ? 'Desativar' : 'Ativar')
                    ->icon(fn (User $record): Heroicon => $record->is_active ? Heroicon::OutlinedNoSymbol : Heroicon::OutlinedCheckCircle)
                    ->color(fn (User $record): string => $record->is_active ? 'danger' : 'success')
                    ->authorize(fn (User $record): bool => Gate::allows($record->is_active ? 'deactivate' : 'activate', $record))
                    ->requiresConfirmation()
                    ->action(function (User $record, Action $action): void {
                        Gate::authorize($record->is_active ? 'deactivate' : 'activate', $record);

                        $record->update(['is_active' => ! $record->is_active]);
                        $action->success();
                    })
                    ->successNotificationTitle(fn (User $record): string => $record->is_active
                        ? 'Usuário ativado'
                        : 'Usuário desativado'),
                EditAction::make()
                    ->authorize('update')
                    ->using(function (User $record, array $data): User {
                        self::guardAccessChanges($record, $data);
                        $record->update($data);

                        return $record;
                    }),
                DeleteAction::make()->authorize('delete'),
            ]);
    }

    public static function getNavigationLabel(): string
    {
        return 'Usuários';
    }

    public static function getModelLabel(): string
    {
        return 'usuário';
    }

    public static function getPluralModelLabel(): string
    {
        return 'usuários';
    }

    public static function getPages(): array
    {
        return [
            'index' => ManageUsers::route('/'),
        ];
    }

    /** @return array<string, string> */
    private static function roleOptions(): array
    {
        return collect(UserRole::cases())
            ->mapWithKeys(fn (UserRole $role): array => [$role->value => $role->label()])
            ->all();
    }

    /** @param array<string, mixed> $data */
    private static function guardAccessChanges(User $record, array $data): void
    {
        $willBeActive = array_key_exists('is_active', $data)
            ? (bool) $data['is_active']
            : $record->is_active;
        $newRole = UserRole::tryFrom($data['role'] ?? $record->role->value) ?? $record->role;

        if ($record->is(auth()->user()) && ! $willBeActive) {
            throw ValidationException::withMessages([
                'is_active' => 'Você não pode desativar o próprio usuário.',
            ]);
        }

        if ($record->isLastActiveAdmin() && ((! $willBeActive) || $newRole !== UserRole::Admin)) {
            throw ValidationException::withMessages([
                'role' => 'O sistema deve manter pelo menos um administrador ativo.',
            ]);
        }
    }
}
