<?php

namespace App\Filament\Resources;

use App\Filament\Resources\TenantResource\Pages;
use App\Filament\Resources\TenantResource\RelationManagers;
use App\Http\Controllers\RbacDelegatedAuthController;
use App\Jobs\BulkTenantSyncJob;
use App\Jobs\SyncPoliciesJob;
use App\Models\Tenant;
use App\Models\User;
use App\Services\Directory\EntraGroupLabelResolver;
use App\Services\Graph\GraphClientInterface;
use App\Services\Intune\AuditLogger;
use App\Services\Intune\RbacHealthService;
use App\Services\Intune\RbacOnboardingService;
use App\Services\Intune\TenantConfigService;
use App\Services\Intune\TenantPermissionService;
use App\Services\OperationRunService;
use App\Services\Operations\BulkSelectionIdentity;
use App\Support\Badges\BadgeDomain;
use App\Support\Badges\BadgeRenderer;
use App\Support\Badges\TagBadgeDomain;
use App\Support\Badges\TagBadgeRenderer;
use App\Support\OperationRunLinks;
use App\Support\OpsUx\OperationUxPresenter;
use App\Support\OpsUx\OpsUxBrowserEvents;
use App\Support\TenantRole;
use BackedEnum;
use Filament\Actions;
use Filament\Actions\ActionGroup;
use Filament\Forms;
use Filament\Infolists;
use Filament\Notifications\Notification;
use Filament\Resources\Resource;
use Filament\Schemas\Components\Utilities\Get;
use Filament\Schemas\Components\Utilities\Set;
use Filament\Schemas\Schema;
use Filament\Tables;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use Throwable;
use UnitEnum;

class TenantResource extends Resource
{
    // ... [Properties Omitted for Brevity] ...
    protected static ?string $model = Tenant::class;

    protected static bool $isScopedToTenant = false;

    protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-building-office-2';

    protected static string|UnitEnum|null $navigationGroup = 'Settings';

    public static function form(Schema $schema): Schema
    {
        // ... [Schema Omitted - No Change] ...
        return $schema
            ->schema([
                Forms\Components\TextInput::make('name')
                    ->required()
                    ->maxLength(255),
                Forms\Components\Select::make('environment')
                    ->options([
                        'prod' => 'PROD',
                        'dev' => 'DEV',
                        'staging' => 'STAGING',
                        'other' => 'Other',
                    ])
                    ->default('other')
                    ->required(),
                Forms\Components\TextInput::make('tenant_id')
                    ->label('Tenant ID (GUID)')
                    ->required()
                    ->maxLength(255)
                    ->unique(ignoreRecord: true),
                Forms\Components\TextInput::make('domain')
                    ->label('Primary domain')
                    ->maxLength(255),
                Forms\Components\TextInput::make('app_client_id')
                    ->label('App Client ID')
                    ->maxLength(255),
                Forms\Components\TextInput::make('app_client_secret')
                    ->label('App Client Secret')
                    ->password()
                    ->dehydrateStateUsing(fn ($state) => filled($state) ? $state : null)
                    ->dehydrated(fn ($state) => filled($state)),
                Forms\Components\TextInput::make('app_certificate_thumbprint')
                    ->label('Certificate thumbprint')
                    ->maxLength(255),
                Forms\Components\Textarea::make('app_notes')
                    ->label('Notes')
                    ->rows(3),
            ]);
    }

    public static function getEloquentQuery(): Builder
    {
        // ... [Query Omitted - No Change] ...
        $user = auth()->user();

        if (! $user instanceof User) {
            return parent::getEloquentQuery()->whereRaw('1 = 0');
        }

        $tenantIds = $user->tenants()
            ->withTrashed()
            ->pluck('tenants.id');

        return parent::getEloquentQuery()
            ->withTrashed()
            ->whereIn('id', $tenantIds)
            ->withCount('policies')
            ->withMax('policies as last_policy_sync_at', 'last_synced_at');
    }

    public static function table(Table $table): Table
    {
        return $table
            ->columns([
                Tables\Columns\TextColumn::make('name')
                    ->searchable(),
                Tables\Columns\TextColumn::make('tenant_id')
                    ->label('Tenant ID')
                    ->copyable()
                    ->searchable(),
                Tables\Columns\TextColumn::make('environment')
                    ->badge()
                    ->formatStateUsing(TagBadgeRenderer::label(TagBadgeDomain::TenantEnvironment))
                    ->color(TagBadgeRenderer::color(TagBadgeDomain::TenantEnvironment))
                    ->sortable(),
                Tables\Columns\TextColumn::make('policies_count')
                    ->label('Policies')
                    ->numeric()
                    ->sortable(),
                Tables\Columns\TextColumn::make('last_policy_sync_at')
                    ->label('Last Sync')
                    ->since()
                    ->sortable(),
                Tables\Columns\TextColumn::make('domain')
                    ->copyable()
                    ->toggleable(),
                Tables\Columns\IconColumn::make('is_current')
                    ->label('Current')
                    ->boolean(),
                Tables\Columns\TextColumn::make('status')
                    ->badge()
                    ->formatStateUsing(BadgeRenderer::label(BadgeDomain::TenantStatus))
                    ->color(BadgeRenderer::color(BadgeDomain::TenantStatus))
                    ->icon(BadgeRenderer::icon(BadgeDomain::TenantStatus))
                    ->iconColor(BadgeRenderer::iconColor(BadgeDomain::TenantStatus))
                    ->sortable(),
                Tables\Columns\TextColumn::make('app_status')
                    ->badge()
                    ->formatStateUsing(BadgeRenderer::label(BadgeDomain::TenantAppStatus))
                    ->color(BadgeRenderer::color(BadgeDomain::TenantAppStatus))
                    ->icon(BadgeRenderer::icon(BadgeDomain::TenantAppStatus))
                    ->iconColor(BadgeRenderer::iconColor(BadgeDomain::TenantAppStatus)),
                Tables\Columns\TextColumn::make('created_at')
                    ->dateTime()
                    ->since(),
            ])
            ->filters([
                Tables\Filters\TrashedFilter::make()
                    ->label('Archived')
                    ->placeholder('Active')
                    ->trueLabel('All')
                    ->falseLabel('Archived')
                    ->default(true),
                Tables\Filters\SelectFilter::make('environment')
                    ->options([
                        'prod' => 'PROD',
                        'dev' => 'DEV',
                        'staging' => 'STAGING',
                        'other' => 'Other',
                    ]),
                Tables\Filters\SelectFilter::make('app_status')
                    ->options([
                        'ok' => 'OK',
                        'consent_required' => 'Consent required',
                        'error' => 'Error',
                        'unknown' => 'Unknown',
                    ]),
            ])
            ->actions([
                Actions\ViewAction::make(),
                ActionGroup::make([
                    Actions\Action::make('syncTenant')
                        ->label('Sync')
                        ->icon('heroicon-o-arrow-path')
                        ->color('warning')
                        ->requiresConfirmation()
                        ->visible(function (Tenant $record): bool {
                            if (! $record->isActive()) {
                                return false;
                            }

                            $user = auth()->user();

                            if (! $user instanceof User) {
                                return false;
                            }

                            return $user->canSyncTenant($record);
                        })
                        ->action(function (Tenant $record, AuditLogger $auditLogger, \Filament\Tables\Contracts\HasTable $livewire): void {
                            // Phase 3: Canonical Operation Run Start
                            /** @var OperationRunService $opService */
                            $opService = app(OperationRunService::class);
                            $opRun = $opService->ensureRun(
                                tenant: $record,
                                type: 'policy.sync',
                                inputs: ['scope' => 'full'],
                                initiator: auth()->user()
                            );

                            if (! $opRun->wasRecentlyCreated && in_array($opRun->status, ['queued', 'running'], true)) {
                                Notification::make()
                                    ->title('Policy sync already active')
                                    ->body('This operation is already queued or running.')
                                    ->warning()
                                    ->actions([
                                        Actions\Action::make('view_run')
                                            ->label('View Run')
                                            ->url(OperationRunLinks::view($opRun, $record)),
                                    ])
                                    ->send();

                                return;
                            }

                            SyncPoliciesJob::dispatch($record->getKey(), null, $opRun);

                            $auditLogger->log(
                                tenant: $record,
                                action: 'tenant.sync_dispatched',
                                resourceType: 'tenant',
                                resourceId: (string) $record->id,
                                status: 'success',
                                context: ['metadata' => ['tenant_id' => $record->tenant_id]],
                            );

                            OpsUxBrowserEvents::dispatchRunEnqueued($livewire);
                            OperationUxPresenter::queuedToast((string) $opRun->type)
                                ->actions([
                                    Actions\Action::make('view_run')
                                        ->label('View Run')
                                        ->url(OperationRunLinks::view($opRun, $record)),
                                ])
                                ->send();
                        }),
                    Actions\Action::make('openTenant')
                        ->label('Open')
                        ->icon('heroicon-o-arrow-right')
                        ->color('primary')
                        ->url(fn (Tenant $record) => \App\Filament\Resources\PolicyResource::getUrl('index', tenant: $record))
                        ->visible(fn (Tenant $record) => $record->isActive()),
                    Actions\EditAction::make(),
                    Actions\RestoreAction::make()
                        ->label('Restore')
                        ->color('success')
                        ->successNotificationTitle('Tenant reactivated')
                        ->after(function (Tenant $record, AuditLogger $auditLogger) {
                            $auditLogger->log(
                                tenant: $record,
                                action: 'tenant.restored',
                                resourceType: 'tenant',
                                resourceId: (string) $record->id,
                                status: 'success',
                                context: ['metadata' => ['tenant_id' => $record->tenant_id]]
                            );
                        }),
                    Actions\Action::make('makeCurrent')
                        ->label('Make current')
                        ->color('success')
                        ->icon('heroicon-o-check-circle')
                        ->requiresConfirmation()
                        ->visible(fn (Tenant $record) => $record->isActive() && ! $record->is_current)
                        ->action(function (Tenant $record, AuditLogger $auditLogger) {
                            $record->makeCurrent();

                            $auditLogger->log(
                                tenant: $record,
                                action: 'tenant.current_set',
                                resourceType: 'tenant',
                                resourceId: (string) $record->id,
                                status: 'success',
                                context: ['metadata' => ['tenant_id' => $record->tenant_id]]
                            );

                            Notification::make()
                                ->title('Current tenant updated')
                                ->success()
                                ->send();
                        }),
                    Actions\Action::make('admin_consent')
                        ->label('Admin consent')
                        ->icon('heroicon-o-clipboard-document')
                        ->url(fn (Tenant $record) => static::adminConsentUrl($record))
                        ->visible(fn (Tenant $record) => static::adminConsentUrl($record) !== null)
                        ->openUrlInNewTab(),
                    Actions\Action::make('open_in_entra')
                        ->label('Open in Entra')
                        ->icon('heroicon-o-arrow-top-right-on-square')
                        ->url(fn (Tenant $record) => static::entraUrl($record))
                        ->visible(fn (Tenant $record) => static::entraUrl($record) !== null)
                        ->openUrlInNewTab(),
                    Actions\Action::make('verify')
                        ->label('Verify configuration')
                        ->icon('heroicon-o-check-badge')
                        ->color('primary')
                        ->requiresConfirmation()
                        ->action(function (
                            Tenant $record,
                            TenantConfigService $configService,
                            TenantPermissionService $permissionService,
                            RbacHealthService $rbacHealthService,
                            AuditLogger $auditLogger
                        ) {
                            static::verifyTenant($record, $configService, $permissionService, $rbacHealthService, $auditLogger);
                        }),
                    static::rbacAction(),
                    Actions\Action::make('archive')
                        ->label('Deactivate')
                        ->color('danger')
                        ->icon('heroicon-o-archive-box-x-mark')
                        ->requiresConfirmation()
                        ->visible(fn (Tenant $record) => ! $record->trashed())
                        ->action(function (Tenant $record, AuditLogger $auditLogger) {
                            $record->delete();

                            $auditLogger->log(
                                tenant: $record,
                                action: 'tenant.archived',
                                resourceType: 'tenant',
                                resourceId: (string) $record->id,
                                status: 'success',
                                context: ['metadata' => ['tenant_id' => $record->tenant_id]]
                            );

                            Notification::make()
                                ->title('Tenant deactivated')
                                ->body('The tenant has been archived and hidden from lists.')
                                ->success()
                                ->send();
                        }),
                    Actions\Action::make('forceDelete')
                        ->label('Force delete')
                        ->color('danger')
                        ->icon('heroicon-o-trash')
                        ->requiresConfirmation()
                        ->visible(fn (?Tenant $record) => $record?->trashed())
                        ->action(function (?Tenant $record, AuditLogger $auditLogger) {
                            if ($record === null) {
                                return;
                            }

                            $tenant = Tenant::withTrashed()->find($record->id);

                            if (! $tenant?->trashed()) {
                                Notification::make()
                                    ->title('Tenant must be archived first')
                                    ->danger()
                                    ->send();

                                return;
                            }

                            $auditLogger->log(
                                tenant: $tenant,
                                action: 'tenant.force_deleted',
                                resourceType: 'tenant',
                                resourceId: (string) $tenant->id,
                                status: 'success',
                                context: ['metadata' => ['tenant_id' => $tenant->tenant_id]]
                            );

                            $tenant->forceDelete();

                            Notification::make()
                                ->title('Tenant permanently deleted')
                                ->success()
                                ->send();
                        }),
                ])->icon('heroicon-o-ellipsis-vertical'),
            ])
            ->bulkActions([
                Actions\BulkAction::make('syncSelected')
                    ->label('Sync selected')
                    ->icon('heroicon-o-arrow-path')
                    ->color('warning')
                    ->requiresConfirmation()
                    ->visible(function (): bool {
                        $user = auth()->user();

                        if (! $user instanceof User) {
                            return false;
                        }

                        return $user->tenants()
                            ->whereIn('role', [
                                TenantRole::Owner->value,
                                TenantRole::Manager->value,
                                TenantRole::Operator->value,
                            ])
                            ->exists();
                    })
                    ->authorize(function (): bool {
                        $user = auth()->user();

                        if (! $user instanceof User) {
                            return false;
                        }

                        return $user->tenants()
                            ->whereIn('role', [
                                TenantRole::Owner->value,
                                TenantRole::Manager->value,
                                TenantRole::Operator->value,
                            ])
                            ->exists();
                    })
                    ->action(function (Collection $records, AuditLogger $auditLogger): void {
                        $user = auth()->user();

                        if (! $user instanceof User) {
                            return;
                        }

                        $eligible = $records
                            ->filter(fn ($record) => $record instanceof Tenant && $record->isActive())
                            ->filter(fn (Tenant $tenant) => $user->canSyncTenant($tenant));

                        if ($eligible->isEmpty()) {
                            Notification::make()
                                ->title('Bulk sync skipped')
                                ->body('No eligible tenants selected.')
                                ->icon('heroicon-o-information-circle')
                                ->info()
                                ->sendToDatabase($user)
                                ->send();

                            return;
                        }

                        $tenantContext = Tenant::current() ?? $eligible->first();

                        if (! $tenantContext) {
                            return;
                        }

                        $ids = $eligible->pluck('id')->toArray();
                        $count = $eligible->count();

                        /** @var BulkSelectionIdentity $selection */
                        $selection = app(BulkSelectionIdentity::class);
                        $selectionIdentity = $selection->fromIds($ids);

                        /** @var OperationRunService $runs */
                        $runs = app(OperationRunService::class);

                        $opRun = $runs->enqueueBulkOperation(
                            tenant: $tenantContext,
                            type: 'tenant.sync',
                            targetScope: [
                                'entra_tenant_id' => (string) ($tenantContext->tenant_id ?? $tenantContext->external_id),
                            ],
                            selectionIdentity: $selectionIdentity,
                            dispatcher: function ($operationRun) use ($tenantContext, $user, $ids): void {
                                BulkTenantSyncJob::dispatch(
                                    tenantId: (int) $tenantContext->getKey(),
                                    userId: (int) $user->getKey(),
                                    tenantIds: $ids,
                                    operationRun: $operationRun,
                                );
                            },
                            initiator: $user,
                            extraContext: [
                                'tenant_count' => $count,
                            ],
                            emitQueuedNotification: false,
                        );

                        OperationUxPresenter::queuedToast('tenant.sync')
                            ->actions([
                                Actions\Action::make('view_run')
                                    ->label('View run')
                                    ->url(OperationRunLinks::view($opRun, $tenantContext)),
                            ])
                            ->send();
                    })
                    ->deselectRecordsAfterCompletion(),
            ])
            ->headerActions([]);
    }

    public static function infolist(Schema $schema): Schema
    {
        // ... [Infolist Omitted - No Change] ...
        return $schema
            ->schema([
                Infolists\Components\TextEntry::make('name'),
                Infolists\Components\TextEntry::make('tenant_id')->label('Tenant ID')->copyable(),
                Infolists\Components\TextEntry::make('domain')->copyable(),
                Infolists\Components\TextEntry::make('app_client_id')->label('App Client ID')->copyable(),
                Infolists\Components\TextEntry::make('status')
                    ->badge()
                    ->formatStateUsing(BadgeRenderer::label(BadgeDomain::TenantStatus))
                    ->color(BadgeRenderer::color(BadgeDomain::TenantStatus))
                    ->icon(BadgeRenderer::icon(BadgeDomain::TenantStatus))
                    ->iconColor(BadgeRenderer::iconColor(BadgeDomain::TenantStatus)),
                Infolists\Components\TextEntry::make('app_status')
                    ->badge()
                    ->formatStateUsing(BadgeRenderer::label(BadgeDomain::TenantAppStatus))
                    ->color(BadgeRenderer::color(BadgeDomain::TenantAppStatus))
                    ->icon(BadgeRenderer::icon(BadgeDomain::TenantAppStatus))
                    ->iconColor(BadgeRenderer::iconColor(BadgeDomain::TenantAppStatus)),
                Infolists\Components\TextEntry::make('app_notes')->label('Notes'),
                Infolists\Components\TextEntry::make('created_at')->dateTime(),
                Infolists\Components\TextEntry::make('updated_at')->dateTime(),
                Infolists\Components\TextEntry::make('rbac_status')
                    ->label('RBAC status')
                    ->badge()
                    ->formatStateUsing(BadgeRenderer::label(BadgeDomain::TenantRbacStatus))
                    ->color(BadgeRenderer::color(BadgeDomain::TenantRbacStatus))
                    ->icon(BadgeRenderer::icon(BadgeDomain::TenantRbacStatus))
                    ->iconColor(BadgeRenderer::iconColor(BadgeDomain::TenantRbacStatus)),
                Infolists\Components\TextEntry::make('rbac_status_reason')->label('RBAC reason'),
                Infolists\Components\TextEntry::make('rbac_last_checked_at')->label('RBAC last checked')->since(),
                Infolists\Components\TextEntry::make('rbac_role_display_name')->label('RBAC role'),
                Infolists\Components\TextEntry::make('rbac_role_definition_id')->label('Role definition ID')->copyable(),
                Infolists\Components\TextEntry::make('rbac_scope_mode')->label('RBAC scope'),
                Infolists\Components\TextEntry::make('rbac_scope_id')->label('Scope ID'),
                Infolists\Components\TextEntry::make('rbac_group_id')->label('RBAC group ID')->copyable(),
                Infolists\Components\TextEntry::make('rbac_role_assignment_id')->label('Role assignment ID')->copyable(),
                Infolists\Components\ViewEntry::make('rbac_summary')
                    ->label('Last RBAC Setup')
                    ->view('filament.infolists.entries.rbac-summary')
                    ->visible(fn (Tenant $record) => filled($record->rbac_last_setup_at)),
                Infolists\Components\TextEntry::make('admin_consent_url')
                    ->label('Admin consent URL')
                    ->state(fn (Tenant $record) => static::adminConsentUrl($record))
                    ->visible(fn (?string $state) => filled($state))
                    ->copyable(),
                Infolists\Components\RepeatableEntry::make('permissions')
                    ->label('Required permissions')
                    ->state(fn (Tenant $record) => app(TenantPermissionService::class)->compare($record, persist: false, useConfiguredStub: false)['permissions'])
                    ->schema([
                        Infolists\Components\TextEntry::make('key')->label('Permission')->badge(),
                        Infolists\Components\TextEntry::make('type')->badge(),
                        Infolists\Components\TextEntry::make('features')
                            ->label('Features')
                            ->formatStateUsing(fn ($state) => is_array($state) ? implode(', ', $state) : (string) $state),
                        Infolists\Components\TextEntry::make('status')
                            ->badge()
                            ->formatStateUsing(BadgeRenderer::label(BadgeDomain::TenantPermissionStatus))
                            ->color(BadgeRenderer::color(BadgeDomain::TenantPermissionStatus))
                            ->icon(BadgeRenderer::icon(BadgeDomain::TenantPermissionStatus))
                            ->iconColor(BadgeRenderer::iconColor(BadgeDomain::TenantPermissionStatus)),
                    ])
                    ->columnSpanFull(),
            ]);
    }

    public static function getPages(): array
    {
        return [
            'index' => Pages\ListTenants::route('/'),
            'create' => Pages\CreateTenant::route('/create'),
            'view' => Pages\ViewTenant::route('/{record}'),
            'edit' => Pages\EditTenant::route('/{record}/edit'),
        ];
    }

    public static function getRelations(): array
    {
        return [
            RelationManagers\TenantMembershipsRelationManager::class,
        ];
    }

    public static function rbacAction(): Actions\Action
    {
        // ... [RBAC Action Omitted - No Change] ...
        return Actions\Action::make('setup_rbac')
            ->label('Setup Intune RBAC')
            ->icon('heroicon-o-shield-check')
            ->color('primary')
            ->form([
                Forms\Components\Select::make('role_definition_id')
                    ->label('RBAC role')
                    ->required()
                    ->searchable()
                    ->optionsLimit(20)
                    ->searchDebounce(400)
                    ->getSearchResultsUsing(fn (string $search, ?Tenant $record) => static::roleSearchOptions($record, $search))
                    ->getOptionLabelUsing(fn (?string $value, ?Tenant $record) => static::formatRoleLabel(
                        static::resolveRoleName($record, $value),
                        $value ?? ''
                    ))
                    ->disabled(fn (?Tenant $record) => static::delegatedToken($record) === null)
                    ->helperText(fn (?Tenant $record) => static::roleSearchHelper($record))
                    ->hintAction(fn (?Tenant $record) => static::loginToSearchRolesAction($record))
                    ->hint('Wizard grants Intune RBAC roles only. "Intune Administrator" is an Entra directory role and is not assigned here.')
                    ->noSearchResultsMessage('No Intune RBAC roleDefinitions found (tenant may restrict RBAC or missing permission).')
                    ->loadingMessage('Loading roles...')
                    ->afterStateUpdated(function (Set $set, ?string $state, ?Tenant $record) {
                        $set('role_display_name', static::resolveRoleName($record, $state));
                    }),
                Forms\Components\Hidden::make('role_display_name')
                    ->dehydrated(),
                Forms\Components\Select::make('scope')
                    ->label('Scope')
                    ->required()
                    ->options([
                        'all_devices' => 'All devices (global)',
                        'scope_group' => 'Scope group (enter ID)',
                    ])
                    ->default('all_devices')
                    ->live(),
                Forms\Components\Select::make('scope_group_id')
                    ->label('Scope group')
                    ->searchable()
                    ->searchPrompt('Type at least 2 characters')
                    ->optionsLimit(20)
                    ->searchDebounce(400)
                    ->placeholder('Search security groups')
                    ->visible(fn (Get $get) => $get('scope') === 'scope_group')
                    ->required(fn (Get $get) => $get('scope') === 'scope_group')
                    ->disabled(fn (?Tenant $record) => static::delegatedToken($record) === null)
                    ->helperText(fn (?Tenant $record) => static::groupSearchHelper($record))
                    ->hintAction(fn (?Tenant $record) => static::loginToSearchGroupsAction($record))
                    ->getSearchResultsUsing(fn (string $search, ?Tenant $record) => static::groupSearchOptions($record, $search))
                    ->getOptionLabelUsing(fn (?string $value, ?Tenant $record) => static::resolveGroupLabel($record, $value))
                    ->noSearchResultsMessage('No security groups found')
                    ->loadingMessage('Searching groups...'),
                Forms\Components\Select::make('group_mode')
                    ->label('Group mode')
                    ->required()
                    ->options([
                        'create' => 'Create new security group',
                        'existing' => 'Use existing security group',
                    ])
                    ->default('create')
                    ->live(),
                Forms\Components\TextInput::make('group_name')
                    ->label('Group name')
                    ->default('TenantPilot-Intune-RBAC')
                    ->visible(fn (callable $get) => $get('group_mode') === 'create'),
                Forms\Components\Select::make('existing_group_id')
                    ->label('Security group')
                    ->searchable()
                    ->searchPrompt('Type at least 2 characters')
                    ->optionsLimit(20)
                    ->searchDebounce(400)
                    ->placeholder('Search security groups')
                    ->visible(fn (Get $get) => $get('group_mode') === 'existing')
                    ->required(fn (Get $get) => $get('group_mode') === 'existing')
                    ->disabled(fn (?Tenant $record) => static::delegatedToken($record) === null)
                    ->helperText(fn (?Tenant $record) => static::groupSearchHelper($record))
                    ->hintAction(fn (?Tenant $record) => static::loginToSearchGroupsAction($record))
                    ->getSearchResultsUsing(fn (string $search, ?Tenant $record) => static::groupSearchOptions($record, $search))
                    ->getOptionLabelUsing(fn (?string $value, ?Tenant $record) => static::resolveGroupLabel($record, $value))
                    ->noSearchResultsMessage('No security groups found')
                    ->loadingMessage('Searching groups...'),
            ])
            ->visible(fn (Tenant $record) => $record->isActive())
            ->requiresConfirmation()
            ->action(function (
                array $data,
                Tenant $record,
                RbacOnboardingService $service,
                AuditLogger $auditLogger
            ) {
                $cacheKey = RbacDelegatedAuthController::cacheKey($record, auth()->id(), session()->getId());
                $token = Cache::get($cacheKey);

                if (! $token) {
                    Notification::make()
                        ->title('Login to grant RBAC')
                        ->body('Delegated login required to continue.')
                        ->actions([
                            Actions\Action::make('open_rbac_login')
                                ->label('Open RBAC login')
                                ->url(route('admin.rbac.start', [
                                    'tenant' => $record->graphTenantId(),
                                    'return' => route('filament.admin.resources.tenants.view', [
                                        'tenant' => $record->external_id,
                                        'record' => $record,
                                    ]),
                                ])),
                        ])
                        ->warning()
                        ->persistent()
                        ->send();

                    return;
                }

                $actor = auth()->user();

                $result = $service->run($record, $data, $actor, $token);

                Cache::forget($cacheKey);

                if ($result['status'] === 'success') {
                    Notification::make()
                        ->title('RBAC setup completed')
                        ->body(sprintf(
                            'Role: %s | Scope: %s | Group: %s | Assignment: %s',
                            $data['role_display_name'] ?? $data['role_definition_id'] ?? 'n/a',
                            $data['scope'] ?? 'n/a',
                            $result['group_id'] ?? 'n/a',
                            $result['role_assignment_id'] ?? 'n/a'
                        ))
                        ->success()
                        ->send();

                    if (($data['scope'] ?? null) === 'scope_group') {
                        Notification::make()
                            ->title('Scope-limited selection')
                            ->body('RBAC scope is limited to a scope group; inventory/restore may be partial.')
                            ->warning()
                            ->send();
                    }

                    if (config('tenantpilot.features.conditional_access', false) === false) {
                        Notification::make()
                            ->title('CA canary disabled')
                            ->body('Conditional Access canary is disabled by feature flag.')
                            ->warning()
                            ->send();
                    }

                    return;
                }

                $auditLogger->log(
                    tenant: $record,
                    action: 'rbac.setup.failed',
                    resourceType: 'tenant',
                    resourceId: (string) $record->id,
                    status: 'error',
                    context: ['metadata' => ['error' => $result['message'] ?? 'unknown']],
                );

                Notification::make()
                    ->title('RBAC setup failed')
                    ->body($result['message'] ?? 'Unknown error')
                    ->danger()
                    ->send();
            });
    }

    public static function adminConsentUrl(Tenant $tenant): ?string
    {
        $tenantId = $tenant->graphTenantId();
        $clientId = $tenant->app_client_id;
        $redirectUri = route('admin.consent.callback');
        $state = sprintf('tenantpilot|%s', $tenant->id);

        if (! $tenantId || ! $clientId || ! $redirectUri) {
            return null;
        }

        // Build explicit scope list from required permissions
        $requiredPermissions = config('intune_permissions.permissions', []);
        $scopes = collect($requiredPermissions)
            ->pluck('key')
            ->map(fn (string $permission) => "https://graph.microsoft.com/{$permission}")
            ->join(' ');

        // Fallback to .default if no permissions configured
        if (empty($scopes)) {
            $scopes = 'https://graph.microsoft.com/.default';
        }

        $query = http_build_query([
            'client_id' => $clientId,
            'state' => $state,
            'redirect_uri' => $redirectUri,
            'scope' => $scopes,
        ]);

        return sprintf('https://login.microsoftonline.com/%s/v2.0/adminconsent?%s', $tenantId, $query);
    }

    public static function entraUrl(Tenant $tenant): ?string
    {
        if ($tenant->app_client_id) {
            return sprintf(
                'https://entra.microsoft.com/#view/Microsoft_AAD_RegisteredApps/ApplicationMenuBlade/~/Overview/appId/%s',
                $tenant->app_client_id
            );
        }

        if ($tenant->graphTenantId()) {
            return sprintf(
                'https://entra.microsoft.com/#view/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/~/Overview/tenantId/%s',
                $tenant->graphTenantId()
            );
        }

        return null;
    }

    private static function delegatedToken(?Tenant $tenant): ?string
    {
        if (! $tenant) {
            return null;
        }

        $userKey = RbacDelegatedAuthController::cacheKey($tenant, auth()->id(), null);
        $sessionKey = RbacDelegatedAuthController::cacheKey($tenant, auth()->id(), session()->getId());

        return Cache::get($userKey) ?? Cache::get($sessionKey);
    }

    private static function loginToSearchRolesAction(?Tenant $tenant): ?Actions\Action
    {
        if (! $tenant) {
            return null;
        }

        return Actions\Action::make('login_to_load_roles')
            ->label('Login to load roles')
            ->url(route('admin.rbac.start', [
                'tenant' => $tenant->graphTenantId(),
                'return' => route('filament.admin.resources.tenants.view', [
                    'tenant' => $tenant->external_id,
                    'record' => $tenant,
                ]),
            ]));
    }

    public static function roleSearchHelper(?Tenant $tenant): ?string
    {
        return static::delegatedToken($tenant) ? null : 'Login to load roles';
    }

    /**
     * @return array<string, string>
     */
    public static function roleSearchOptions(?Tenant $tenant, string $search): array
    {
        return static::searchRoleDefinitions($tenant, $search);
    }

    /**
     * @return array<string, string>
     */
    private static function searchRoleDefinitions(?Tenant $tenant, string $search): array
    {
        if (! $tenant) {
            return [];
        }

        $token = static::delegatedToken($tenant);

        if (! $token) {
            return [];
        }

        if (Str::contains(Str::lower($search), 'intune administrator')) {
            Notification::make()
                ->title('Intune Administrator is a directory role')
                ->body('Das ist eine Entra Directory Role, nicht Intune RBAC; wird vom Wizard nicht vergeben.')
                ->warning()
                ->persistent()
                ->send();
        }

        $filter = mb_strlen($search) >= 2
            ? sprintf("startswith(displayName,'%s')", static::escapeOdataValue($search))
            : null;

        $query = [
            '$select' => 'id,displayName,isBuiltIn',
            '$top' => 20,
        ];

        if ($filter) {
            $query['$filter'] = $filter;
        }

        try {
            $response = app(GraphClientInterface::class)->request(
                'GET',
                'deviceManagement/roleDefinitions',
                [
                    'query' => $query,
                ] + $tenant->graphOptions() + [
                    'access_token' => $token,
                ]
            );
        } catch (Throwable) {
            static::notifyRoleLookupFailure();

            return [];
        }

        if ($response->failed()) {
            static::notifyRoleLookupFailure();

            return [];
        }

        $roles = collect($response->data['value'] ?? [])
            ->filter(fn (array $role) => filled($role['id'] ?? null))
            ->mapWithKeys(fn (array $role) => [
                $role['id'] => static::formatRoleLabel($role['displayName'] ?? null, $role['id']),
            ])
            ->all();

        if (empty($roles)) {
            static::logEmptyRoleDefinitions($tenant, $response->data['value'] ?? []);
        }

        return $roles;
    }

    private static function resolveRoleName(?Tenant $tenant, ?string $roleId): ?string
    {
        if (! $tenant || blank($roleId)) {
            return $roleId;
        }

        $token = static::delegatedToken($tenant);

        if (! $token) {
            return $roleId;
        }

        try {
            $response = app(GraphClientInterface::class)->request(
                'GET',
                "deviceManagement/roleDefinitions/{$roleId}",
                [
                    'query' => [
                        '$select' => 'id,displayName',
                    ],
                ] + $tenant->graphOptions() + [
                    'access_token' => $token,
                ]
            );
        } catch (Throwable) {
            static::notifyRoleLookupFailure();

            return $roleId;
        }

        if ($response->failed()) {
            static::notifyRoleLookupFailure();

            return $roleId;
        }

        $displayName = $response->data['displayName'] ?? null;
        $id = $response->data['id'] ?? $roleId;

        return $displayName ?: $id;
    }

    private static function formatRoleLabel(?string $displayName, string $id): string
    {
        $suffix = sprintf(' (%s)', Str::limit($id, 8, ''));

        return trim(($displayName ?: 'RBAC role').$suffix);
    }

    private static function escapeOdataValue(string $value): string
    {
        return str_replace("'", "''", $value);
    }

    private static function notifyRoleLookupFailure(): void
    {
        Notification::make()
            ->title('Role lookup failed')
            ->body('Delegated session may have expired. Login again to load Intune RBAC roles.')
            ->danger()
            ->send();
    }

    private static function logEmptyRoleDefinitions(Tenant $tenant, array $roles): void
    {
        $names = collect($roles)->pluck('displayName')->filter()->take(5)->values()->all();

        Log::warning('rbac.role_definitions.empty', [
            'tenant_id' => $tenant->id,
            'count' => count($roles),
            'sample' => $names,
        ]);

        try {
            app(AuditLogger::class)->log(
                tenant: $tenant,
                action: 'rbac.roles.empty',
                resourceType: 'tenant',
                resourceId: (string) $tenant->id,
                status: 'warning',
                context: ['metadata' => ['count' => count($roles), 'sample' => $names]],
            );
        } catch (Throwable) {
            Log::notice('rbac.role_definitions.audit_failed', ['tenant_id' => $tenant->id]);
        }
    }

    private static function loginToSearchGroupsAction(?Tenant $tenant): ?Actions\Action
    {
        if (! $tenant) {
            return null;
        }

        return Actions\Action::make('login_to_search_groups')
            ->label('Login to search groups')
            ->url(route('admin.rbac.start', [
                'tenant' => $tenant->graphTenantId(),
                'return' => route('filament.admin.resources.tenants.view', [
                    'tenant' => $tenant->external_id,
                    'record' => $tenant,
                ]),
            ]));
    }

    public static function groupSearchHelper(?Tenant $tenant): ?string
    {
        if (! $tenant) {
            return null;
        }

        return static::delegatedToken($tenant) ? null : 'Login to search groups';
    }

    /**
     * @return array<string, string>
     */
    public static function groupSearchOptions(?Tenant $tenant, string $search): array
    {
        if (! $tenant || mb_strlen($search) < 2) {
            return [];
        }

        $token = static::delegatedToken($tenant);

        if (! $token) {
            return [];
        }

        $filter = sprintf(
            "securityEnabled eq true and startswith(displayName,'%s')",
            static::escapeOdataValue($search)
        );

        try {
            $response = app(GraphClientInterface::class)->request(
                'GET',
                'groups',
                [
                    'query' => [
                        '$select' => 'id,displayName',
                        '$top' => 20,
                        '$filter' => $filter,
                    ],
                ] + $tenant->graphOptions() + [
                    'access_token' => $token,
                ]
            );
        } catch (Throwable) {
            return [];
        }

        if ($response->failed()) {
            return [];
        }

        return collect($response->data['value'] ?? [])
            ->filter(fn (array $group) => filled($group['id'] ?? null))
            ->mapWithKeys(fn (array $group) => [
                (string) $group['id'] => EntraGroupLabelResolver::formatLabel($group['displayName'] ?? null, (string) $group['id']),
            ])
            ->all();
    }

    private static function resolveGroupLabel(?Tenant $tenant, ?string $groupId): ?string
    {
        if (! $tenant || blank($groupId)) {
            return $groupId;
        }

        $token = static::delegatedToken($tenant);

        if (! $token) {
            return $groupId;
        }

        try {
            $response = app(GraphClientInterface::class)->request(
                'GET',
                'groups/'.$groupId,
                [] + $tenant->graphOptions() + [
                    'access_token' => $token,
                ]
            );
        } catch (Throwable) {
            return $groupId;
        }

        if ($response->failed()) {
            return $groupId;
        }

        return EntraGroupLabelResolver::formatLabel(
            $response->data['displayName'] ?? null,
            $response->data['id'] ?? $groupId
        );
    }

    public static function verifyTenant(
        Tenant $tenant,
        TenantConfigService $configService,
        TenantPermissionService $permissionService,
        RbacHealthService $rbacHealthService,
        AuditLogger $auditLogger
    ): void {
        $configResult = $configService->testConnectivity($tenant);
        // Fetch actual permissions from Graph API with liveCheck=true
        $permissions = $permissionService->compare($tenant, null, true, true);
        $rbac = $rbacHealthService->check($tenant);

        $appStatus = $configResult['success']
            ? 'ok'
            : ($configResult['requires_consent'] ? 'consent_required' : 'error');

        $tenant->update([
            'app_status' => $appStatus,
            'app_notes' => $configResult['error_message'],
        ]);

        $user = auth()->user();

        $auditLogger->log(
            tenant: $tenant,
            action: 'tenant.config.verified',
            context: [
                'metadata' => [
                    'app_status' => $appStatus,
                    'error' => $configResult['error_message'],
                ],
            ],
            actorId: $user?->id,
            actorEmail: $user?->email,
            actorName: $user?->name,
            status: $appStatus === 'ok' ? 'success' : 'error',
            resourceType: 'tenant',
            resourceId: (string) $tenant->id,
        );

        $auditLogger->log(
            tenant: $tenant,
            action: 'tenant.permissions.checked',
            context: [
                'metadata' => [
                    'overall_status' => $permissions['overall_status'],
                ],
            ],
            actorId: $user?->id,
            actorEmail: $user?->email,
            actorName: $user?->name,
            status: match ($permissions['overall_status']) {
                'granted' => 'success',
                'error' => 'error',
                default => 'partial',
            },
            resourceType: 'tenant',
            resourceId: (string) $tenant->id,
        );

        $auditLogger->log(
            tenant: $tenant,
            action: 'tenant.rbac.checked',
            context: [
                'metadata' => [
                    'status' => $rbac['status'],
                    'reason' => $rbac['reason'] ?? null,
                ],
            ],
            status: $rbac['status'] === 'ok' ? 'success' : 'error',
            resourceType: 'tenant',
            resourceId: (string) $tenant->id,
        );

        $notification = Notification::make()
            ->title($configResult['success'] ? 'Configuration verified' : 'Verification failed')
            ->body($configResult['success']
                ? 'Graph connectivity confirmed. Permission status: '.$permissions['overall_status']
                : ($configResult['error_message'] ?? 'Graph connectivity failed'));

        if ($configResult['success']) {
            $notification->success();
        } elseif ($configResult['requires_consent']) {
            $notification->warning();
        } else {
            $notification->danger();
        }

        $notification->send();
    }
}
