<?php

declare(strict_types=1);

namespace Builder\Core\Extensions\Resources\Roles;

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\Resource;
use Builder\Core\Foundation\Tables\Column;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Validation\Rule;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;

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

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

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

    /** @return class-string<Model> */
    public static function getModel(): string
    {
        /** @var class-string<Model> */
        return Role::class;
    }

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

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

    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::toggle('locked')->label((string) __('builder::builder.roles.locked')),
                    ]),
                Section::make('permissions')
                    ->label((string) __('builder::builder.sections.permissions'))
                    ->priority(20)
                    ->tab('main')
                    ->fields([
                        Field::checkbox('permissions')
                            ->label((string) __('builder::builder.sections.permissions'))
                            ->multiple()
                            ->options(fn (): array => Permission::orderBy('name')
                                ->pluck('name', 'name')
                                ->all())
                            ->component('PermissionsInput')
                            ->meta('virtual', true),
                    ]),
            );
    }

    /** @return list<Column> */
    public function columns(): array
    {
        return [
            Column::make('name')->label((string) __('builder::builder.fields.name'))->sortable()->searchable(),
            Column::make('locked')
                ->label((string) __('builder::builder.roles.locked'))
                ->format(fn (mixed $value): string => $value ? 'locked' : '')
                ->badge([
                    'locked' => ['label' => (string) __('builder::builder.roles.locked'), 'color' => 'red'],
                ]),
        ];
    }

    /** @return list<string> */
    public function eagerLoad(): array
    {
        return ['permissions'];
    }

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

    /** @return array<string, mixed> */
    public function rules(string $action): array
    {
        $nameUnique = $action === 'update'
            ? Rule::unique('roles', 'name')->ignore(request()->route('id'))
            : Rule::unique('roles', 'name');

        return [
            'name' => ['required', 'string', 'max:255', $nameUnique],
            'locked' => ['boolean'],
            'permissions' => ['nullable', 'array'],
            'permissions.*' => ['string'],
        ];
    }

    public function canDelete(mixed $record): void
    {
        if ($record->locked) {
            throw DeletionDeniedException::withReason((string) __('builder::builder.resources.cannot_delete_locked'));
        }
    }

    /** @return array<string, mixed> */
    public function getRecordForEdit(Model $record): array
    {
        $data = parent::getRecordForEdit($record);

        // The permissions relation returns full Permission objects; the form expects names only.
        if (isset($data['permissions']) && is_array($data['permissions'])) {
            $data['permissions'] = array_column($data['permissions'], 'name');
        }

        return $data;
    }

    /** @return list<Hook> */
    public function hooks(): array
    {
        return [
            /**
             * Unset permissions from the model before saving to prevent
             * Eloquent from trying to persist it as a DB column.
             */
            Hook::on('creating', function (mixed $record, array $data): void {
                $record->offsetUnset('permissions');
                $record->guard_name = 'web';
            }),

            Hook::on('updating', function (mixed $record, array $data): void {
                $record->offsetUnset('permissions');
                $record->guard_name = $record->getOriginal('guard_name') ?? 'web';
            }),

            /**
             * Sync the role's Spatie permissions after the record is saved.
             */
            Hook::on('saved', function (mixed $record, array $data): void {
                if (array_key_exists('permissions', $data) && $record instanceof Model && method_exists($record, 'syncPermissions')) {
                    $record->syncPermissions($data['permissions'] ?? []);
                }
            }),
        ];
    }
}
