<?php

declare(strict_types=1);

namespace Builder\Core\Extensions\Resources\Users;

use Builder\Core\Exceptions\DeletionDeniedException;
use Builder\Core\Foundation\Concerns\HasCommandPaletteSearch;
use Builder\Core\Foundation\Concerns\HasRevisions;
use Builder\Core\Foundation\Contracts\CommandPaletteSearchable;
use Builder\Core\Foundation\Definition\Field;
use Builder\Core\Foundation\Definition\FieldBuilder;
use Builder\Core\Foundation\Definition\Hook;
use Builder\Core\Foundation\Definition\Section;
use Builder\Core\Foundation\Resources\RecordAction;
use Builder\Core\Foundation\Resources\Resource;
use Builder\Core\Foundation\Tables\BulkAction;
use Builder\Core\Foundation\Tables\Column;
use Builder\Core\Foundation\Tables\Filter;
use Builder\Core\Notifications\UserInvitedNotification;
use Illuminate\Auth\Passwords\PasswordBroker;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Auth\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
use Spatie\Permission\Models\Role;

final class UserResource extends Resource implements CommandPaletteSearchable
{
    use HasCommandPaletteSearch;
    use HasRevisions;

    public static function getSlug(): string
    {
        return 'users';
    }

    public static function getLabel(): string
    {
        return (string) __('builder::builder.nav.users');
    }

    /** @return class-string<Model> */
    public static function getModel(): string
    {
        /** @var class-string<Model> */
        return config('auth.providers.users.model', User::class);
    }

    public function getPermission(): string
    {
        return 'read:users';
    }

    /** @return list<string> */
    public function getPermissionSet(): array
    {
        return ['read:users', 'create:users', 'update:users', 'delete:users'];
    }

    public function fields(): FieldBuilder
    {
        return FieldBuilder::make()
            ->add(
                Section::make('general')
                    ->label((string) __('builder::builder.sections.general'))
                    ->priority(10)
                    ->tab('main')
                    ->fields([
                        Field::text('name')->label((string) __('builder::builder.fields.name')),
                        Field::email('email')->label((string) __('builder::builder.fields.email')),
                    ]),
                Section::make('roles')
                    ->label((string) __('builder::builder.sections.roles'))
                    ->priority(20)
                    ->tab('main')
                    ->fields([
                        Field::select('roles')
                            ->label((string) __('builder::builder.sections.roles'))
                            ->options(fn (): array => Role::orderBy('name')
                                ->pluck('name', 'id')
                                ->all()),
                    ]),
            );
    }

    /** @return list<Column> */
    public function columns(): array
    {
        return [
            Column::make('name')->label((string) __('builder::builder.fields.name'))->sortable()->searchable(),
            Column::make('email')->label((string) __('builder::builder.fields.email'))->sortable()->searchable(),
            Column::make('activated_at')
                ->label((string) __('builder::builder.fields.status'))
                ->format(fn (mixed $value): string => $value ? 'active' : 'invited')
                ->badge([
                    'active' => ['label' => (string) __('builder::builder.fields.status_active'), 'color' => 'green'],
                    'invited' => ['label' => (string) __('builder::builder.fields.status_invited'), 'color' => 'yellow'],
                ]),
            Column::make('created_at')->label((string) __('builder::builder.fields.joined'))->sortable()->formatDate(),
        ];
    }

    /** @return list<string> */
    public function eagerLoad(): array
    {
        $model = self::getModel();

        return method_exists(new $model, 'roles') ? ['roles'] : [];
    }

    /** @return list<string> */
    public function searchable(): array
    {
        return ['name', 'email'];
    }

    /** @return array<string, mixed> */
    public function rules(string $action): array
    {
        $model = self::getModel();
        $table = (new $model)->getTable();

        $emailUnique = $action === 'update'
            ? Rule::unique($table, 'email')->ignore(request()->route('id'))
            : 'unique:'.$table.',email';

        return [
            'name' => ['required', 'string', 'max:255'],
            'email' => ['required', 'email', 'max:255', $emailUnique],
            'roles' => ['nullable', 'integer'],
        ];
    }

    /** @return list<Hook> */
    public function hooks(): array
    {
        return [
            /**
             * Set a random unguessable password so the user record can be saved.
             * The real password is set by the user via the invite link.
             */
            Hook::on('creating', function (mixed $record, array $data): void {
                $record->offsetUnset('roles');
                $record->password = Hash::make(Str::random(64));
            }),

            /**
             * Send an invite email after the user record is created.
             */
            Hook::on('created', function (mixed $record): void {
                /** @var PasswordBroker $broker */
                $broker = Password::broker('builder-invite');
                $token = $broker->createToken($record);
                $record->notify(new UserInvitedNotification($token));
            }),

            /**
             * Strip the roles key before updating (handled by saved hook).
             */
            Hook::on('updating', function (mixed $record, array $data): void {
                $record->offsetUnset('roles');
            }),

            /**
             * Sync the user's role relationships after the record is saved.
             */
            Hook::on('saved', function (mixed $record, array $data): void {
                if (array_key_exists('roles', $data) && $record instanceof Model && method_exists($record, 'syncRoles')) {
                    $roleId = $data['roles'] ?: null;
                    $role = $roleId !== null ? Role::find($roleId) : null;
                    $record->syncRoles($role !== null ? [$role] : []);
                }
            }),
        ];
    }

    public function canDelete(mixed $record): void
    {
        $model = self::getModel();

        if ($model::count() <= 1) {
            throw DeletionDeniedException::withReason((string) __('builder::builder.resources.cannot_delete_last_user'));
        }
    }

    /** @return list<Filter> */
    public function filters(): array
    {
        $model = self::getModel();
        $hasRoles = method_exists(new $model, 'roles');

        $filters = [
            Filter::make('status')
                ->label((string) __('builder::builder.fields.status'))
                ->options([
                    'active' => (string) __('builder::builder.fields.status_active'),
                    'invited' => (string) __('builder::builder.fields.status_invited'),
                ])
                ->apply(fn (Builder $query, string $value) => $value === 'active'
                    ? $query->whereNotNull('activated_at')
                    : $query->whereNull('activated_at')),
        ];

        if ($hasRoles) {
            $filters[] = Filter::make('role')
                ->label((string) __('builder::builder.sections.roles'))
                ->options(fn (): array => Role::orderBy('name')->pluck('name', 'name')->all())
                ->apply(fn (Builder $query, string $value) => $query->whereHas('roles', fn (Builder $q) => $q->where('name', $value)));
        }

        return $filters;
    }

    /** @return list<BulkAction> */
    public function bulkActions(): array
    {
        return [
            BulkAction::make('delete')
                ->label((string) __('builder::builder.resources.delete'))
                ->confirm((string) __('builder::builder.resources.delete_confirm_message')),
        ];
    }

    /** @return list<RecordAction> */
    public function recordActions(): array
    {
        return [
            RecordAction::make('resend-invite')
                ->label(fn (): string => (string) __('builder::builder.invite.resend_invite'))
                ->visibleFor(fn (mixed $record): bool => $record->activated_at === null)
                ->handler(function (mixed $record): void {
                    /** @var PasswordBroker $broker */
                    $broker = Password::broker('builder-invite');
                    $token = $broker->createToken($record);
                    $record->notify(new UserInvitedNotification($token));
                }),

            RecordAction::make('send-reset-password')
                ->label(fn (): string => (string) __('builder::builder.invite.send_reset_password'))
                ->visibleFor(fn (mixed $record): bool => $record->activated_at !== null)
                ->handler(function (mixed $record): void {
                    /** @var PasswordBroker $broker */
                    $broker = Password::broker('builder-invite');
                    $token = $broker->createToken($record);
                    $record->notify(new UserInvitedNotification($token, isReset: true));
                }),
        ];
    }
}
