<?php

namespace App\Filament\Forms\Components;

use App\Models\Concerns\HasPermissions;
use App\Services\PermissionCatalog;
use Closure;
use Filament\Forms\Components\ViewField;

class PermissionPicker extends ViewField
{
    /**
     * Whether to cap the rendered matrix at the tenant's entitlement ceiling.
     * Off by default because the landlord's Entitlements editor uses this same
     * component to SET that ceiling and must therefore see the whole catalog.
     */
    protected bool $limitToEntitlement = false;

    /**
     * Explicit tenant to cap the matrix at. Only needed when this field isn't
     * rendered under the acted-on tenant's own `web` guard — e.g. Central's
     * per-tenant role editor, where `Auth::user()` has no `tenant_id` of its
     * own. Null (the default) falls back to `Auth::user()->tenant_id`.
     */
    protected int|Closure|null $tenantId = null;

    protected function setUp(): void
    {
        parent::setUp();

        $this->view('filament.forms.permission-picker');
        $this->columnSpanFull();
        $this->viewData(fn (): array => [
            'groups' => $this->limitToEntitlement
                ? PermissionCatalog::entitledMatrix($this->resolveTenantId())
                : PermissionCatalog::matrix(),
        ]);
    }

    /**
     * Hide permissions the tenant is not entitled to. A tenant-side editor
     * (RoleResource) should never offer a permission that
     * {@see HasPermissions::hasPermissionTo()} will refuse
     * for everyone in the tenant regardless of who is granted it.
     */
    public function limitedToTenantEntitlement(bool $condition = true): static
    {
        $this->limitToEntitlement = $condition;

        return $this;
    }

    /**
     * Cap the rendered matrix at a specific tenant's entitlement ceiling
     * instead of the current auth user's own. Needed by editors that act on a
     * tenant other than the one `Auth::user()` belongs to — e.g. Central's
     * per-tenant role editor, run under the `central` guard.
     */
    public function tenantId(int|Closure|null $tenantId): static
    {
        $this->tenantId = $tenantId;

        return $this;
    }

    protected function resolveTenantId(): ?int
    {
        return $this->tenantId instanceof Closure ? ($this->tenantId)() : $this->tenantId;
    }

    /**
     * @return array<string>
     */
    public function getInValidationRuleValues(): ?array
    {
        return PermissionCatalog::allPermissionNames();
    }

    public function hasInValidationOnMultipleValues(): bool
    {
        return true;
    }
}
