<?php

namespace App\Filament\Resources;

use App\Filament\Concerns\InteractsWithTenantOwnedRecords;
use App\Filament\Concerns\ResolvesPanelTenantContext;
use App\Filament\Concerns\ScopesGlobalSearchToTenant;
use App\Filament\Concerns\WorkspaceScopedEnvironmentRoutes;
use App\Filament\Resources\EntraGroupResource\Pages;
use App\Models\EntraGroup;
use App\Models\ManagedEnvironment;
use App\Support\Badges\BadgeDomain;
use App\Support\Badges\BadgeRenderer;
use App\Support\Filament\TablePaginationProfiles;
use App\Support\Navigation\NavigationScope;
use App\Support\Ui\ActionSurface\ActionSurfaceDeclaration;
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceInspectAffordance;
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceProfile;
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceSlot;
use App\Support\Ui\EnterpriseDetail\EnterpriseDetailBuilder;
use App\Support\Ui\EnterpriseDetail\EnterpriseDetailPageData;
use App\Support\Ui\EnterpriseDetail\EnterpriseDetailSectionFactory;
use App\Support\Ui\EnterpriseDetail\SummaryHeaderData;
use BackedEnum;
use Filament\Facades\Filament;
use Filament\Infolists\Components\ViewEntry;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Tables;
use Filament\Tables\Filters\SelectFilter;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Carbon;
use UnitEnum;

class EntraGroupResource extends Resource
{
    use InteractsWithTenantOwnedRecords;
    use ResolvesPanelTenantContext;
    use ScopesGlobalSearchToTenant;
    use WorkspaceScopedEnvironmentRoutes;

    protected static bool $isScopedToTenant = false;

    protected static ?string $model = EntraGroup::class;

    protected static ?string $tenantOwnershipRelationshipName = 'tenant';

    protected static ?string $recordTitleAttribute = 'display_name';

    protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-user-group';

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

    protected static ?string $navigationLabel = 'Groups';

    public static function shouldRegisterNavigation(): bool
    {
        return NavigationScope::shouldRegisterEnvironmentNavigation()
            && parent::shouldRegisterNavigation();
    }

    public static function actionSurfaceDeclaration(): ActionSurfaceDeclaration
    {
        return ActionSurfaceDeclaration::forResource(ActionSurfaceProfile::CrudListAndView)
            ->exempt(ActionSurfaceSlot::ListHeader, 'Directory groups list intentionally has no header actions; sync is started from the sync-runs surface.')
            ->satisfy(ActionSurfaceSlot::InspectAffordance, ActionSurfaceInspectAffordance::ClickableRow->value)
            ->exempt(ActionSurfaceSlot::ListRowMoreMenu, 'No secondary row actions are provided on this read-only list.')
            ->exempt(ActionSurfaceSlot::ListBulkMoreGroup, 'Bulk actions are intentionally omitted for directory groups.')
            ->exempt(ActionSurfaceSlot::ListEmptyState, 'Empty-state CTA is intentionally omitted; groups appear after sync.')
            ->exempt(ActionSurfaceSlot::DetailHeader, 'No canonical related destination exists for directory groups yet.');
    }

    public static function form(Schema $schema): Schema
    {
        return $schema;
    }

    public static function infolist(Schema $schema): Schema
    {
        return $schema
            ->schema([
                ViewEntry::make('enterprise_detail')
                    ->label('')
                    ->view('filament.infolists.entries.enterprise-detail.layout')
                    ->state(fn (EntraGroup $record): array => static::enterpriseDetailPage($record)->toArray())
                    ->columnSpanFull(),
            ]);
    }

    public static function table(Table $table): Table
    {
        return $table
            ->defaultSort('display_name')
            ->paginated(TablePaginationProfiles::resource())
            ->persistFiltersInSession()
            ->persistSearchInSession()
            ->persistSortInSession()
            ->recordUrl(static fn (EntraGroup $record): ?string => static::canView($record)
                ? static::scopedUrl('view', ['record' => $record], static::panelTenantContext())
                : null)
            ->columns([
                Tables\Columns\TextColumn::make('display_name')
                    ->label('Name')
                    ->searchable()
                    ->sortable(),
                Tables\Columns\TextColumn::make('entra_id')
                    ->label('Entra ID')
                    ->copyable()
                    ->toggleable(isToggledHiddenByDefault: true),
                Tables\Columns\TextColumn::make('type')
                    ->label('Type')
                    ->badge()
                    ->state(fn (EntraGroup $record): string => static::groupTypeLabel(static::groupType($record)))
                    ->color(fn (EntraGroup $record): string => static::groupTypeColor(static::groupType($record))),
                Tables\Columns\TextColumn::make('last_seen_at')
                    ->label('Last seen')
                    ->since(),
            ])
            ->filters([
                SelectFilter::make('stale')
                    ->label('Stale')
                    ->options([
                        '1' => 'Stale',
                        '0' => 'Fresh',
                    ])
                    ->query(function (Builder $query, array $data): Builder {
                        $value = $data['value'] ?? null;

                        if ($value === null || $value === '') {
                            return $query;
                        }

                        $stalenessDays = (int) config('directory_groups.staleness_days', 30);
                        $cutoff = now('UTC')->subDays(max(1, $stalenessDays));

                        if ((string) $value === '1') {
                            return $query->where(function (Builder $q) use ($cutoff): void {
                                $q->whereNull('last_seen_at')
                                    ->orWhere('last_seen_at', '<', $cutoff);
                            });
                        }

                        return $query->where('last_seen_at', '>=', $cutoff);
                    }),
                SelectFilter::make('group_type')
                    ->label('Type')
                    ->options([
                        'security' => 'Security',
                        'microsoft365' => 'Microsoft 365',
                        'mail' => 'Mail-enabled',
                        'unknown' => 'Unknown',
                    ])
                    ->query(function (Builder $query, array $data): Builder {
                        $value = (string) ($data['value'] ?? '');

                        if ($value === '') {
                            return $query;
                        }

                        return match ($value) {
                            'microsoft365' => $query->whereJsonContains('group_types', 'Unified'),
                            'security' => $query
                                ->where('security_enabled', true)
                                ->where(function (Builder $q): void {
                                    $q->whereNull('group_types')
                                        ->orWhereJsonDoesntContain('group_types', 'Unified');
                                }),
                            'mail' => $query
                                ->where('mail_enabled', true)
                                ->where(function (Builder $q): void {
                                    $q->whereNull('group_types')
                                        ->orWhereJsonDoesntContain('group_types', 'Unified');
                                }),
                            'unknown' => $query
                                ->where(function (Builder $q): void {
                                    $q->whereNull('group_types')
                                        ->orWhereJsonDoesntContain('group_types', 'Unified');
                                })
                                ->where('security_enabled', false)
                                ->where('mail_enabled', false),
                            default => $query,
                        };
                    }),
            ])
            ->actions([])
            ->bulkActions([])
            ->emptyStateHeading('No groups cached yet')
            ->emptyStateDescription('No groups are available for this managed environment yet. Run or refresh the relevant directory inventory operation to make groups visible here.')
            ->emptyStateIcon('heroicon-o-user-group');
    }

    public static function getEloquentQuery(): Builder
    {
        return static::getTenantOwnedEloquentQuery()
            ->latest('id');
    }

    public static function resolveScopedRecordOrFail(int|string $key): Model
    {
        return static::resolveTenantOwnedRecordOrFail($key);
    }

    public static function getGlobalSearchResultUrl(Model $record): string
    {
        $tenant = $record instanceof EntraGroup && $record->tenant instanceof ManagedEnvironment
            ? $record->tenant
            : static::panelTenantContext();

        return static::scopedUrl('view', ['record' => $record], $tenant);
    }

    public static function getPages(): array
    {
        return [
            'index' => Pages\ListEntraGroups::route('/'),
            'view' => Pages\ViewEntraGroup::route('/{record}'),
        ];
    }

    /**
     * @param  array<string, mixed>  $parameters
     */
    public static function scopedUrl(
        string $page = 'index',
        array $parameters = [],
        ?ManagedEnvironment $tenant = null,
        ?string $panel = null,
    ): string {
        $panelId = $panel ?? Filament::getCurrentOrDefaultPanel()?->getId() ?? 'admin';

        return static::getUrl($page, $parameters, panel: $panelId, tenant: $tenant);
    }

    private static function groupType(EntraGroup $record): string
    {
        $groupTypes = $record->group_types;

        if (is_array($groupTypes) && in_array('Unified', $groupTypes, true)) {
            return 'microsoft365';
        }

        if ($record->security_enabled) {
            return 'security';
        }

        if ($record->mail_enabled) {
            return 'mail';
        }

        return 'unknown';
    }

    private static function groupTypeLabel(string $type): string
    {
        return match ($type) {
            'microsoft365' => 'Microsoft 365',
            'security' => 'Security',
            'mail' => 'Mail-enabled',
            default => 'Unknown',
        };
    }

    private static function groupTypeColor(string $type): string
    {
        return match ($type) {
            'microsoft365' => 'info',
            'security' => 'success',
            'mail' => 'warning',
            default => 'gray',
        };
    }

    private static function enterpriseDetailPage(EntraGroup $record): EnterpriseDetailPageData
    {
        $factory = new EnterpriseDetailSectionFactory;

        $groupType = static::groupType($record);
        $groupTypeLabel = static::groupTypeLabel($groupType);
        $groupTypeBadge = $factory->statusBadge($groupTypeLabel, static::groupTypeColor($groupType));
        $securityBadge = $factory->statusBadge(
            BadgeRenderer::label(BadgeDomain::BooleanEnabled)($record->security_enabled),
            BadgeRenderer::color(BadgeDomain::BooleanEnabled)($record->security_enabled),
            BadgeRenderer::icon(BadgeDomain::BooleanEnabled)($record->security_enabled),
            BadgeRenderer::iconColor(BadgeDomain::BooleanEnabled)($record->security_enabled),
        );
        $mailBadge = $factory->statusBadge(
            BadgeRenderer::label(BadgeDomain::BooleanEnabled)($record->mail_enabled),
            BadgeRenderer::color(BadgeDomain::BooleanEnabled)($record->mail_enabled),
            BadgeRenderer::icon(BadgeDomain::BooleanEnabled)($record->mail_enabled),
            BadgeRenderer::iconColor(BadgeDomain::BooleanEnabled)($record->mail_enabled),
        );

        $technicalPayload = [
            'entra_id' => $record->entra_id,
            'group_types' => is_array($record->group_types) ? $record->group_types : [],
        ];

        return EnterpriseDetailBuilder::make('entra_group', 'tenant')
            ->header(new SummaryHeaderData(
                title: (string) $record->display_name,
                subtitle: 'Directory group #'.$record->getKey(),
                statusBadges: [$groupTypeBadge, $securityBadge, $mailBadge],
                keyFacts: [
                    $factory->keyFact('Type', $groupTypeLabel, badge: $groupTypeBadge),
                    $factory->keyFact('Last seen', static::formatDetailTimestamp($record->last_seen_at)),
                    $factory->keyFact('Security enabled', $record->security_enabled, badge: $securityBadge),
                    $factory->keyFact('Mail enabled', $record->mail_enabled, badge: $mailBadge),
                ],
                descriptionHint: 'Group identity and classification stay ahead of provider-oriented metadata.',
            ))
            ->addSection(
                $factory->factsSection(
                    id: 'classification_overview',
                    kind: 'core_details',
                    title: 'Classification overview',
                    items: [
                        $factory->keyFact('Type', $groupTypeLabel, badge: $groupTypeBadge),
                        $factory->keyFact('Security enabled', $record->security_enabled, badge: $securityBadge),
                        $factory->keyFact('Mail enabled', $record->mail_enabled, badge: $mailBadge),
                        $factory->keyFact('Last seen', static::formatDetailTimestamp($record->last_seen_at)),
                    ],
                ),
                $factory->viewSection(
                    id: 'related_context',
                    kind: 'related_context',
                    title: 'Related context',
                    view: 'filament.infolists.entries.related-context',
                    viewData: ['entries' => []],
                    emptyState: $factory->emptyState('No related context is available for this record.'),
                ),
            )
            ->addSupportingCard(
                $factory->supportingFactsCard(
                    kind: 'summary',
                    title: 'Directory identity',
                    items: [
                        $factory->keyFact('Display name', $record->display_name),
                        $factory->keyFact('Entra ID', $record->entra_id),
                    ],
                ),
                $factory->supportingFactsCard(
                    kind: 'timestamps',
                    title: 'Freshness',
                    items: [
                        $factory->keyFact('Last seen', static::formatDetailTimestamp($record->last_seen_at)),
                        $factory->keyFact('Cached group types', count($technicalPayload['group_types'])),
                    ],
                ),
            )
            ->addTechnicalSection(
                $factory->technicalDetail(
                    title: 'Technical detail',
                    entries: [
                        $factory->keyFact('Entra ID', $record->entra_id),
                        $factory->keyFact('Cached group types', count($technicalPayload['group_types'])),
                    ],
                    description: 'Provider identifiers and raw group-type arrays stay secondary to group identity and classification.',
                    view: 'filament.infolists.entries.snapshot-json',
                    viewData: ['payload' => $technicalPayload],
                ),
            )
            ->build();
    }

    private static function formatDetailTimestamp(mixed $value): string
    {
        if (! $value instanceof Carbon) {
            return '—';
        }

        return $value->toDayDateTimeString();
    }
}
