<?php

namespace App\Filament\Resources;

use App\Filament\Clusters\Inventory\InventoryCluster;
use App\Filament\Concerns\InteractsWithTenantOwnedRecords;
use App\Filament\Concerns\ResolvesPanelTenantContext;
use App\Filament\Concerns\WorkspaceScopedEnvironmentRoutes;
use App\Filament\Resources\InventoryItemResource\Pages;
use App\Models\InventoryItem;
use App\Models\ManagedEnvironment;
use App\Models\User;
use App\Services\Auth\CapabilityResolver;
use App\Support\Auth\Capabilities;
use App\Support\Badges\BadgeDomain;
use App\Support\Badges\BadgeRenderer;
use App\Support\Badges\TagBadgeDomain;
use App\Support\Badges\TagBadgeRenderer;
use App\Support\Filament\FilterOptionCatalog;
use App\Support\Inventory\InventoryPolicyTypeMeta;
use App\Support\Navigation\NavigationScope;
use App\Support\OperationRunLinks;
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 BackedEnum;
use Closure;
use Filament\Facades\Filament;
use Filament\Infolists\Components\TextEntry;
use Filament\Infolists\Components\ViewEntry;
use Filament\Panel;
use Filament\Resources\Resource;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Schema;
use Filament\Tables;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Str;
use UnitEnum;

class InventoryItemResource extends Resource
{
    use InteractsWithTenantOwnedRecords;
    use ResolvesPanelTenantContext;
    use WorkspaceScopedEnvironmentRoutes;

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

    protected static ?string $tenantOwnershipRelationshipName = 'tenant';

    protected static ?string $cluster = InventoryCluster::class;

    protected static ?int $navigationSort = 1;

    protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-rectangle-stack';

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

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

    public static function getRouteBaseName(?Panel $panel = null): string
    {
        $panel ??= Filament::getCurrentOrDefaultPanel();

        if ($panel->getId() !== 'admin') {
            return parent::getRouteBaseName($panel);
        }

        return $panel->generateRouteName(
            (string) str(static::getSlug($panel))
                ->replace('/', '.')
                ->prepend('resources.'),
        );
    }

    public static function registerRoutes(Panel $panel, ?Closure $registerPageRoutes = null): void
    {
        if ($panel->getId() !== 'admin') {
            parent::registerRoutes($panel, $registerPageRoutes);

            return;
        }

        $registerPageRoutes ??= function () use ($panel): void {
            foreach (static::getPages() as $name => $page) {
                $page->registerRoute($panel)?->name($name);
            }
        };

        Route::name('resources.')->group(fn () => static::routes($panel, $registerPageRoutes));
    }

    public static function actionSurfaceDeclaration(): ActionSurfaceDeclaration
    {
        return ActionSurfaceDeclaration::forResource(ActionSurfaceProfile::ListOnlyReadOnly)
            ->exempt(ActionSurfaceSlot::ListHeader, 'Inventory items list intentionally has no header actions; items are populated via sync.')
            ->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 inventory items.')
            ->exempt(ActionSurfaceSlot::ListEmptyState, 'Empty-state CTA is intentionally omitted; inventory items are created by inventory sync.');
    }

    public static function canViewAny(): bool
    {
        $tenant = static::resolveTenantContextForCurrentPanel();
        $user = auth()->user();

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

        $capabilityResolver = app(CapabilityResolver::class);

        return $capabilityResolver->isMember($user, $tenant)
            && $capabilityResolver->can($user, $tenant, Capabilities::TENANT_VIEW);
    }

    public static function canView(Model $record): bool
    {
        $tenant = static::resolveTenantContextForCurrentPanel();
        $user = auth()->user();

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

        $capabilityResolver = app(CapabilityResolver::class);

        if (! $capabilityResolver->isMember($user, $tenant)) {
            return false;
        }

        if (! $capabilityResolver->can($user, $tenant, Capabilities::TENANT_VIEW)) {
            return false;
        }

        if ($record instanceof InventoryItem) {
            return (int) $record->managed_environment_id === (int) $tenant->getKey();
        }

        return true;
    }

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

    public static function infolist(Schema $schema): Schema
    {
        return $schema
            ->schema([
                Section::make('Artifact source')
                    ->schema([
                        TextEntry::make('artifact_source_family')
                            ->label('Source family')
                            ->badge()
                            ->state(fn (InventoryItem $record): string => static::artifactDescriptorValue($record, 'source_family'))
                            ->formatStateUsing(fn (string $state): string => Str::headline($state)),
                        TextEntry::make('artifact_source_kind')
                            ->label('Source kind')
                            ->state(fn (InventoryItem $record): string => static::artifactDescriptorValue($record, 'source_kind'))
                            ->formatStateUsing(fn (string $state): string => Str::headline($state)),
                        TextEntry::make('artifact_source_target')
                            ->label('Source target')
                            ->state(fn (InventoryItem $record): string => static::artifactDescriptorValue($record, 'source_target_kind'))
                            ->formatStateUsing(fn (string $state): string => Str::headline($state)),
                        TextEntry::make('artifact_control_key')
                            ->label('Control')
                            ->state(fn (InventoryItem $record): ?string => static::artifactDescriptorNullableValue($record, 'control_key'))
                            ->formatStateUsing(fn (string $state): string => Str::headline($state))
                            ->placeholder('—'),
                    ])
                    ->columns(2)
                    ->columnSpanFull(),

                Section::make('Inventory Item')
                    ->schema([
                        TextEntry::make('display_name')->label('Name'),
                        TextEntry::make('canonical_type')
                            ->label('Canonical type')
                            ->badge()
                            ->state(fn (InventoryItem $record): string => static::typeDescriptorValue($record, 'canonical_type'))
                            ->formatStateUsing(fn (string $state): string => Str::headline($state)),
                        TextEntry::make('provider_display_type')
                            ->label('Provider display type')
                            ->state(fn (InventoryItem $record): string => static::typeDescriptorValue($record, 'provider_display_type')),
                        TextEntry::make('provider_object_type')
                            ->label('Provider object type')
                            ->state(fn (InventoryItem $record): string => static::typeDescriptorValue($record, 'provider_object_type'))
                            ->copyable(),
                        TextEntry::make('policy_type')
                            ->label('Legacy policy type')
                            ->badge()
                            ->formatStateUsing(TagBadgeRenderer::label(TagBadgeDomain::PolicyType))
                            ->color(TagBadgeRenderer::color(TagBadgeDomain::PolicyType)),
                        TextEntry::make('category')
                            ->badge()
                            ->state(fn (InventoryItem $record): ?string => $record->category
                                ?: (static::typeMeta($record->policy_type)['category'] ?? null))
                            ->formatStateUsing(TagBadgeRenderer::label(TagBadgeDomain::PolicyCategory))
                            ->color(TagBadgeRenderer::color(TagBadgeDomain::PolicyCategory)),
                        TextEntry::make('platform')
                            ->badge()
                            ->formatStateUsing(TagBadgeRenderer::label(TagBadgeDomain::Platform))
                            ->color(TagBadgeRenderer::color(TagBadgeDomain::Platform)),
                        TextEntry::make('external_id')->label('External ID'),
                        TextEntry::make('last_seen_at')->label('Last seen')->dateTime(),
                        TextEntry::make('last_seen_operation_run_id')
                            ->label('Last inventory sync')
                            ->visible(fn (InventoryItem $record): bool => filled($record->last_seen_operation_run_id))
                            ->url(function (InventoryItem $record): ?string {
                                if (! $record->last_seen_operation_run_id) {
                                    return null;
                                }

                                $tenant = $record->tenant;

                                if ($tenant instanceof ManagedEnvironment) {
                                    return OperationRunLinks::view((int) $record->last_seen_operation_run_id, $tenant);
                                }

                                return OperationRunLinks::tenantlessView((int) $record->last_seen_operation_run_id);
                            })
                            ->openUrlInNewTab(),
                        TextEntry::make('support_restore')
                            ->label('Restore')
                            ->badge()
                            ->state(fn (InventoryItem $record): string => static::typeMeta($record->policy_type)['restore'] ?? 'enabled')
                            ->formatStateUsing(BadgeRenderer::label(BadgeDomain::PolicyRestoreMode))
                            ->color(BadgeRenderer::color(BadgeDomain::PolicyRestoreMode))
                            ->icon(BadgeRenderer::icon(BadgeDomain::PolicyRestoreMode))
                            ->iconColor(BadgeRenderer::iconColor(BadgeDomain::PolicyRestoreMode)),
                        TextEntry::make('support_risk')
                            ->label('Risk')
                            ->badge()
                            ->state(fn (InventoryItem $record): string => static::typeMeta($record->policy_type)['risk'] ?? 'normal')
                            ->formatStateUsing(BadgeRenderer::label(BadgeDomain::PolicyRisk))
                            ->color(BadgeRenderer::color(BadgeDomain::PolicyRisk))
                            ->icon(BadgeRenderer::icon(BadgeDomain::PolicyRisk))
                            ->iconColor(BadgeRenderer::iconColor(BadgeDomain::PolicyRisk)),
                    ])
                    ->columns(2)
                    ->columnSpanFull(),

                Section::make('Dependencies')
                    ->schema([
                        ViewEntry::make('dependencies')
                            ->label('')
                            ->view('filament.components.dependency-edges')
                            ->columnSpanFull(),
                    ])
                    ->columnSpanFull(),

                Section::make('Metadata (Safe Subset)')
                    ->schema([
                        ViewEntry::make('meta_jsonb')
                            ->label('')
                            ->view('filament.infolists.entries.snapshot-json')
                            ->state(fn (InventoryItem $record) => $record->meta_jsonb ?? [])
                            ->columnSpanFull(),
                    ])
                    ->columnSpanFull(),
            ]);
    }

    public static function table(Table $table): Table
    {
        $typeOptions = FilterOptionCatalog::policyTypes();

        $categoryOptions = collect(static::allTypeMeta())
            ->mapWithKeys(fn (array $row) => [($row['category'] ?? null) => ($row['category'] ?? null)])
            ->filter(fn ($value, $key) => is_string($key) && $key !== '')
            ->all();

        return $table
            ->defaultSort('last_seen_at', 'desc')
            ->paginated(\App\Support\Filament\TablePaginationProfiles::resource())
            ->persistFiltersInSession()
            ->persistSearchInSession()
            ->persistSortInSession()
            ->columns([
                Tables\Columns\TextColumn::make('display_name')
                    ->label('Name')
                    ->searchable()
                    ->sortable(),
                Tables\Columns\TextColumn::make('canonical_type')
                    ->label('Canonical type')
                    ->badge()
                    ->getStateUsing(fn (InventoryItem $record): string => static::typeDescriptorValue($record, 'canonical_type'))
                    ->formatStateUsing(fn (string $state): string => Str::headline($state)),
                Tables\Columns\TextColumn::make('provider_display_type')
                    ->label('Provider display type')
                    ->getStateUsing(fn (InventoryItem $record): string => static::typeDescriptorValue($record, 'provider_display_type')),
                Tables\Columns\TextColumn::make('policy_type')
                    ->label('Legacy policy type')
                    ->badge()
                    ->formatStateUsing(TagBadgeRenderer::label(TagBadgeDomain::PolicyType))
                    ->color(TagBadgeRenderer::color(TagBadgeDomain::PolicyType))
                    ->toggleable(isToggledHiddenByDefault: true),
                Tables\Columns\TextColumn::make('category')
                    ->badge()
                    ->formatStateUsing(TagBadgeRenderer::label(TagBadgeDomain::PolicyCategory))
                    ->color(TagBadgeRenderer::color(TagBadgeDomain::PolicyCategory)),
                Tables\Columns\TextColumn::make('platform')
                    ->badge()
                    ->formatStateUsing(TagBadgeRenderer::label(TagBadgeDomain::Platform))
                    ->color(TagBadgeRenderer::color(TagBadgeDomain::Platform)),
                Tables\Columns\TextColumn::make('last_seen_at')
                    ->label('Last seen')
                    ->since(),
                Tables\Columns\TextColumn::make('lastSeenRun.outcome')
                    ->label('Run')
                    ->badge()
                    ->formatStateUsing(function (?string $state): string {
                        if (! filled($state)) {
                            return '—';
                        }

                        return BadgeRenderer::spec(BadgeDomain::OperationRunOutcome, $state)->label;
                    })
                    ->color(function (?string $state): string {
                        if (! filled($state)) {
                            return 'gray';
                        }

                        return BadgeRenderer::spec(BadgeDomain::OperationRunOutcome, $state)->color;
                    })
                    ->icon(function (?string $state): ?string {
                        if (! filled($state)) {
                            return null;
                        }

                        return BadgeRenderer::spec(BadgeDomain::OperationRunOutcome, $state)->icon;
                    })
                    ->iconColor(function (?string $state): ?string {
                        if (! filled($state)) {
                            return 'gray';
                        }

                        $spec = BadgeRenderer::spec(BadgeDomain::OperationRunOutcome, $state);

                        return $spec->iconColor ?? $spec->color;
                    }),
            ])
            ->filters([
                Tables\Filters\SelectFilter::make('policy_type')
                    ->options($typeOptions)
                    ->searchable(),
                Tables\Filters\SelectFilter::make('category')
                    ->options($categoryOptions)
                    ->searchable(),
                Tables\Filters\SelectFilter::make('platform')
                    ->options(FilterOptionCatalog::platforms())
                    ->searchable(),
                Tables\Filters\SelectFilter::make('stale')
                    ->label('Freshness')
                    ->options([
                        '0' => 'Fresh',
                        '1' => 'Stale',
                    ])
                    ->query(function (Builder $query, array $data): Builder {
                        $value = $data['value'] ?? null;

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

                        $cutoff = now()->subHours(max(1, (int) config('tenantpilot.hardening.intune_write_gate.freshness_threshold_hours', 24)));

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

                        return $query->where('last_seen_at', '>=', $cutoff);
                    }),
            ])
            ->recordUrl(static fn (Model $record): ?string => static::canView($record)
                ? static::getUrl('view', ['record' => $record])
                : null)
            ->actions([])
            ->bulkActions([])
            ->emptyStateHeading('No inventory items')
            ->emptyStateDescription('Run an inventory sync to capture policy state for this tenant.')
            ->emptyStateIcon('heroicon-o-clipboard-document-list');
    }

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

    public static function resolveScopedRecordOrFail(int|string $key): Model
    {
        $tenant = static::resolveTenantContextForCurrentPanelOrFail();

        return static::resolveTenantOwnedRecordOrFail(
            $key,
            parent::getEloquentQuery()->with('lastSeenRun'),
            $tenant,
        );
    }

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

    /**
     * @return array{label:?string,category:?string,restore:?string,risk:?string}|array<string,mixed>
     */
    private static function typeMeta(?string $type): array
    {
        if ($type === null) {
            return [];
        }

        return InventoryPolicyTypeMeta::metaFor($type);
    }

    private static function typeDescriptorValue(InventoryItem $record, string $key): string
    {
        $descriptor = $record->inventoryTypeDescriptor();
        $value = $descriptor[$key] ?? null;

        return is_string($value) && trim($value) !== '' ? trim($value) : 'unknown';
    }

    private static function artifactDescriptorValue(InventoryItem $record, string $key): string
    {
        return (string) (static::artifactDescriptorNullableValue($record, $key) ?? 'unknown');
    }

    private static function artifactDescriptorNullableValue(InventoryItem $record, string $key): ?string
    {
        $descriptor = $record->artifactSourceDescriptor()->toArray();
        $value = $descriptor[$key] ?? null;

        return is_string($value) && trim($value) !== '' ? trim($value) : null;
    }

    /**
     * @return array<int, array<string, mixed>>
     */
    private static function allTypeMeta(): array
    {
        return InventoryPolicyTypeMeta::all();
    }
}
