<?php

declare(strict_types=1);

namespace App\Filament\Resources;

use App\Filament\Resources\BaselineProfileResource\Pages;
use App\Models\BaselineProfile;
use App\Models\User;
use App\Models\Workspace;
use App\Services\Audit\WorkspaceAuditLogger;
use App\Support\Audit\AuditActionId;
use App\Support\Auth\Capabilities;
use App\Support\Badges\BadgeDomain;
use App\Support\Badges\BadgeRenderer;
use App\Support\Baselines\BaselineCaptureMode;
use App\Support\Baselines\BaselineFullContentRolloutGate;
use App\Support\Baselines\BaselineProfileStatus;
use App\Support\Filament\FilterOptionCatalog;
use App\Support\Inventory\InventoryPolicyTypeMeta;
use App\Support\Rbac\WorkspaceUiEnforcement;
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\Workspaces\WorkspaceContext;
use BackedEnum;
use Filament\Actions\Action;
use Filament\Actions\ActionGroup;
use Filament\Actions\BulkActionGroup;
use Filament\Facades\Filament;
use Filament\Forms\Components\Placeholder;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Infolists\Components\TextEntry;
use Filament\Notifications\Notification;
use Filament\Resources\Resource;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Schema;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Validation\Rule;
use Illuminate\Validation\Rules\Unique;
use UnitEnum;

class BaselineProfileResource extends Resource
{
    protected static bool $isDiscovered = false;

    protected static bool $isScopedToTenant = false;

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

    protected static ?string $slug = 'baseline-profiles';

    protected static bool $isGloballySearchable = false;

    protected static ?string $recordTitleAttribute = 'name';

    protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-shield-check';

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

    protected static ?string $navigationLabel = 'Baselines';

    protected static ?int $navigationSort = 1;

    public static function shouldRegisterNavigation(): bool
    {
        if (Filament::getCurrentPanel()?->getId() !== 'admin') {
            return false;
        }

        return parent::shouldRegisterNavigation();
    }

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

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

        $workspace = self::resolveWorkspace();

        if (! $workspace instanceof Workspace) {
            return false;
        }

        $resolver = app(\App\Services\Auth\WorkspaceCapabilityResolver::class);

        return $resolver->isMember($user, $workspace)
            && $resolver->can($user, $workspace, Capabilities::WORKSPACE_BASELINES_VIEW);
    }

    public static function canCreate(): bool
    {
        return self::hasManageCapability();
    }

    public static function canEdit(Model $record): bool
    {
        return self::hasManageCapability();
    }

    public static function canDelete(Model $record): bool
    {
        return self::hasManageCapability();
    }

    public static function canView(Model $record): bool
    {
        return self::canViewAny();
    }

    public static function actionSurfaceDeclaration(): ActionSurfaceDeclaration
    {
        return ActionSurfaceDeclaration::forResource(ActionSurfaceProfile::CrudListAndView)
            ->satisfy(ActionSurfaceSlot::ListHeader, 'Header action: Create baseline profile (capability-gated).')
            ->satisfy(ActionSurfaceSlot::InspectAffordance, ActionSurfaceInspectAffordance::ViewAction->value)
            ->satisfy(ActionSurfaceSlot::ListRowMoreMenu, 'Secondary row actions (edit, archive) under "More".')
            ->exempt(ActionSurfaceSlot::ListBulkMoreGroup, 'No bulk mutations for baseline profiles in v1.')
            ->satisfy(ActionSurfaceSlot::ListEmptyState, 'List defines empty-state create CTA.')
            ->satisfy(ActionSurfaceSlot::DetailHeader, 'View page provides capture + edit actions.');
    }

    public static function getEloquentQuery(): Builder
    {
        $workspaceId = app(WorkspaceContext::class)->currentWorkspaceId(request());

        return parent::getEloquentQuery()
            ->with(['activeSnapshot', 'createdByUser'])
            ->when(
                $workspaceId !== null,
                fn (Builder $query): Builder => $query->where('workspace_id', (int) $workspaceId),
            )
            ->when(
                $workspaceId === null,
                fn (Builder $query): Builder => $query->whereRaw('1 = 0'),
            );
    }

    public static function form(Schema $schema): Schema
    {
        return $schema
            ->schema([
                Section::make('Profile')
                    ->schema([
                        TextInput::make('name')
                            ->required()
                            ->maxLength(255)
                            ->rule(fn (?BaselineProfile $record): Unique => Rule::unique('baseline_profiles', 'name')
                                ->where('workspace_id', $record?->workspace_id ?? app(WorkspaceContext::class)->currentWorkspaceId(request()))
                                ->ignore($record))
                            ->helperText('A descriptive name for this baseline profile.'),
                        Textarea::make('description')
                            ->rows(3)
                            ->maxLength(1000)
                            ->helperText('Explain the purpose and scope of this baseline.'),
                    ]),
                Section::make('Controls')
                    ->schema([
                        Select::make('status')
                            ->required()
                            ->options(fn (?BaselineProfile $record): array => self::statusOptionsForRecord($record))
                            ->default(BaselineProfileStatus::Draft->value)
                            ->native(false)
                            ->disabled(fn (?BaselineProfile $record): bool => $record?->status === BaselineProfileStatus::Archived)
                            ->helperText(fn (?BaselineProfile $record): string => match ($record?->status) {
                                BaselineProfileStatus::Archived => 'Archived baselines cannot be reactivated.',
                                BaselineProfileStatus::Active => 'Changing status to Archived is permanent.',
                                default => 'Only active baselines are enforced during compliance checks.',
                            }),
                        Select::make('capture_mode')
                            ->label('Capture mode')
                            ->required()
                            ->options(BaselineCaptureMode::selectOptions())
                            ->default(BaselineCaptureMode::Opportunistic->value)
                            ->native(false)
                            ->disabled(fn (?BaselineProfile $record): bool => $record?->status === BaselineProfileStatus::Archived)
                            ->disableOptionWhen(function (string $value): bool {
                                if ($value !== BaselineCaptureMode::FullContent->value) {
                                    return false;
                                }

                                return ! app(BaselineFullContentRolloutGate::class)->enabled();
                            })
                            ->helperText(fn (): string => app(BaselineFullContentRolloutGate::class)->enabled()
                                ? 'Full content capture enables deep drift detection by capturing policy evidence on demand.'
                                : 'Full content capture is currently disabled by rollout configuration.'),
                        TextInput::make('version_label')
                            ->label('Version label')
                            ->maxLength(50)
                            ->placeholder('e.g. v2.1 — February rollout')
                            ->helperText('Optional label to identify this version.'),
                        Select::make('scope_jsonb.policy_types')
                            ->label('Policy types')
                            ->multiple()
                            ->options(self::policyTypeOptions())
                            ->helperText('Leave empty to include all supported policy types (excluding foundations).')
                            ->native(false),
                        Select::make('scope_jsonb.foundation_types')
                            ->label('Foundations')
                            ->multiple()
                            ->options(self::foundationTypeOptions())
                            ->helperText('Leave empty to exclude foundations. Select foundations to include them.')
                            ->native(false),
                        Placeholder::make('metadata')
                            ->label('Last modified')
                            ->content(fn (?BaselineProfile $record): string => $record?->updated_at
                                ? $record->updated_at->diffForHumans()
                                : '—')
                            ->visible(fn (?BaselineProfile $record): bool => $record !== null),
                    ])
                    ->columns(2),
            ]);
    }

    public static function infolist(Schema $schema): Schema
    {
        return $schema
            ->schema([
                Section::make('Profile')
                    ->schema([
                        TextEntry::make('name'),
                        TextEntry::make('status')
                            ->badge()
                            ->formatStateUsing(BadgeRenderer::label(BadgeDomain::BaselineProfileStatus))
                            ->color(BadgeRenderer::color(BadgeDomain::BaselineProfileStatus))
                            ->icon(BadgeRenderer::icon(BadgeDomain::BaselineProfileStatus)),
                        TextEntry::make('capture_mode')
                            ->label('Capture mode')
                            ->badge()
                            ->formatStateUsing(function (mixed $state): string {
                                if ($state instanceof BaselineCaptureMode) {
                                    return $state->label();
                                }

                                $parsed = is_string($state) ? BaselineCaptureMode::tryFrom($state) : null;

                                return $parsed?->label() ?? (is_string($state) ? $state : '—');
                            })
                            ->color(function (mixed $state): string {
                                $mode = $state instanceof BaselineCaptureMode
                                    ? $state
                                    : (is_string($state) ? BaselineCaptureMode::tryFrom($state) : null);

                                return match ($mode) {
                                    BaselineCaptureMode::FullContent => 'success',
                                    BaselineCaptureMode::Opportunistic => 'warning',
                                    BaselineCaptureMode::MetaOnly => 'gray',
                                    default => 'gray',
                                };
                            }),
                        TextEntry::make('version_label')
                            ->label('Version')
                            ->placeholder('—'),
                        TextEntry::make('description')
                            ->placeholder('No description')
                            ->columnSpanFull(),
                    ])
                    ->columns(2)
                    ->columnSpanFull(),
                Section::make('Scope')
                    ->schema([
                        TextEntry::make('scope_jsonb.policy_types')
                            ->label('Policy types')
                            ->badge()
                            ->formatStateUsing(function (string $state): string {
                                $options = self::policyTypeOptions();

                                return $options[$state] ?? $state;
                            })
                            ->placeholder('All supported policy types (excluding foundations)'),
                        TextEntry::make('scope_jsonb.foundation_types')
                            ->label('Foundations')
                            ->badge()
                            ->formatStateUsing(function (string $state): string {
                                $options = self::foundationTypeOptions();

                                return $options[$state] ?? $state;
                            })
                            ->placeholder('None'),
                    ])
                    ->columnSpanFull(),
                Section::make('Metadata')
                    ->schema([
                        TextEntry::make('createdByUser.name')
                            ->label('Created by')
                            ->placeholder('—'),
                        TextEntry::make('activeSnapshot.captured_at')
                            ->label('Last snapshot')
                            ->dateTime()
                            ->placeholder('No snapshot yet'),
                        TextEntry::make('created_at')
                            ->dateTime(),
                        TextEntry::make('updated_at')
                            ->dateTime(),
                    ])
                    ->columns(2)
                    ->columnSpanFull(),
            ]);
    }

    public static function table(Table $table): Table
    {
        $workspace = self::resolveWorkspace();

        return $table
            ->defaultSort('name')
            ->paginated(\App\Support\Filament\TablePaginationProfiles::resource())
            ->columns([
                TextColumn::make('name')
                    ->searchable()
                    ->sortable(),
                TextColumn::make('status')
                    ->badge()
                    ->formatStateUsing(BadgeRenderer::label(BadgeDomain::BaselineProfileStatus))
                    ->color(BadgeRenderer::color(BadgeDomain::BaselineProfileStatus))
                    ->icon(BadgeRenderer::icon(BadgeDomain::BaselineProfileStatus))
                    ->sortable(),
                TextColumn::make('capture_mode')
                    ->label('Capture mode')
                    ->badge()
                    ->formatStateUsing(function (mixed $state): string {
                        if ($state instanceof BaselineCaptureMode) {
                            return $state->label();
                        }

                        $parsed = is_string($state) ? BaselineCaptureMode::tryFrom($state) : null;

                        return $parsed?->label() ?? (is_string($state) ? $state : '—');
                    })
                    ->color(function (mixed $state): string {
                        $mode = $state instanceof BaselineCaptureMode
                            ? $state
                            : (is_string($state) ? BaselineCaptureMode::tryFrom($state) : null);

                        return match ($mode) {
                            BaselineCaptureMode::FullContent => 'success',
                            BaselineCaptureMode::Opportunistic => 'warning',
                            BaselineCaptureMode::MetaOnly => 'gray',
                            default => 'gray',
                        };
                    })
                    ->toggleable(isToggledHiddenByDefault: true),
                TextColumn::make('version_label')
                    ->label('Version')
                    ->placeholder('—'),
                TextColumn::make('tenant_assignments_count')
                    ->label('Assigned tenants')
                    ->counts('tenantAssignments'),
                TextColumn::make('activeSnapshot.captured_at')
                    ->label('Last snapshot')
                    ->dateTime()
                    ->placeholder('No snapshot'),
                TextColumn::make('created_at')
                    ->dateTime()
                    ->sortable()
                    ->toggleable(isToggledHiddenByDefault: true),
            ])
            ->filters([
                \Filament\Tables\Filters\SelectFilter::make('status')
                    ->options(FilterOptionCatalog::baselineProfileStatuses()),
            ])
            ->actions([
                Action::make('view')
                    ->label('View')
                    ->url(fn (BaselineProfile $record): string => static::getUrl('view', ['record' => $record]))
                    ->icon('heroicon-o-eye'),
                ActionGroup::make([
                    Action::make('edit')
                        ->label('Edit')
                        ->url(fn (BaselineProfile $record): string => static::getUrl('edit', ['record' => $record]))
                        ->icon('heroicon-o-pencil-square')
                        ->visible(fn (): bool => self::hasManageCapability()),
                    self::archiveTableAction($workspace),
                ])->label('More'),
            ])
            ->bulkActions([
                BulkActionGroup::make([])->label('More'),
            ])
            ->emptyStateHeading('No baseline profiles')
            ->emptyStateDescription('Create a baseline profile to define what "good" looks like for your tenants.')
            ->emptyStateActions([
                Action::make('create')
                    ->label('Create baseline profile')
                    ->url(fn (): string => static::getUrl('create'))
                    ->icon('heroicon-o-plus')
                    ->visible(fn (): bool => self::hasManageCapability()),
            ]);
    }

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

    public static function getPages(): array
    {
        return [
            'index' => Pages\ListBaselineProfiles::route('/'),
            'create' => Pages\CreateBaselineProfile::route('/create'),
            'view' => Pages\ViewBaselineProfile::route('/{record}'),
            'edit' => Pages\EditBaselineProfile::route('/{record}/edit'),
        ];
    }

    /**
     * @return array<string, string>
     */
    public static function policyTypeOptions(): array
    {
        return collect(InventoryPolicyTypeMeta::supported())
            ->filter(fn (array $row): bool => filled($row['type'] ?? null))
            ->mapWithKeys(fn (array $row): array => [
                (string) $row['type'] => (string) ($row['label'] ?? $row['type']),
            ])
            ->sort()
            ->all();
    }

    /**
     * @return array<string, string>
     */
    public static function foundationTypeOptions(): array
    {
        return collect(InventoryPolicyTypeMeta::baselineSupportedFoundations())
            ->filter(fn (array $row): bool => filled($row['type'] ?? null))
            ->mapWithKeys(fn (array $row): array => [
                (string) $row['type'] => InventoryPolicyTypeMeta::baselineCompareLabel((string) $row['type']) ?? (string) ($row['label'] ?? $row['type']),
            ])
            ->sort()
            ->all();
    }

    /**
     * @param  array<string, mixed>  $metadata
     */
    public static function audit(BaselineProfile $record, AuditActionId $actionId, array $metadata): void
    {
        $workspace = $record->workspace;

        if ($workspace === null) {
            return;
        }

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

        app(WorkspaceAuditLogger::class)->log(
            workspace: $workspace,
            action: $actionId->value,
            context: ['metadata' => $metadata],
            actor: $actor instanceof User ? $actor : null,
            resourceType: 'baseline_profile',
            resourceId: (string) $record->getKey(),
        );
    }

    /**
     * Status options scoped to valid transitions from the current record state.
     *
     * @return array<string, string>
     */
    private static function statusOptionsForRecord(?BaselineProfile $record): array
    {
        if ($record === null) {
            return [BaselineProfileStatus::Draft->value => BaselineProfileStatus::Draft->label()];
        }

        $currentStatus = $record->status instanceof BaselineProfileStatus
            ? $record->status
            : (BaselineProfileStatus::tryFrom((string) $record->status) ?? BaselineProfileStatus::Draft);

        return $currentStatus->selectOptions();
    }

    private static function resolveWorkspace(): ?Workspace
    {
        $workspaceId = app(WorkspaceContext::class)->currentWorkspaceId(request());

        if ($workspaceId === null) {
            return null;
        }

        return Workspace::query()->whereKey($workspaceId)->first();
    }

    private static function hasManageCapability(): bool
    {
        $user = auth()->user();
        $workspace = self::resolveWorkspace();

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

        $resolver = app(\App\Services\Auth\WorkspaceCapabilityResolver::class);

        return $resolver->isMember($user, $workspace)
            && $resolver->can($user, $workspace, Capabilities::WORKSPACE_BASELINES_MANAGE);
    }

    private static function archiveTableAction(?Workspace $workspace): Action
    {
        $action = Action::make('archive')
            ->label('Archive')
            ->icon('heroicon-o-archive-box')
            ->color('warning')
            ->requiresConfirmation()
            ->modalHeading('Archive baseline profile')
            ->modalDescription('Archiving is permanent in v1. This profile can no longer be used for captures or compares.')
            ->hidden(fn (BaselineProfile $record): bool => $record->status === BaselineProfileStatus::Archived)
            ->action(function (BaselineProfile $record): void {
                if (! self::hasManageCapability()) {
                    throw new AuthorizationException;
                }

                $record->forceFill(['status' => BaselineProfileStatus::Archived->value])->save();

                self::audit($record, AuditActionId::BaselineProfileArchived, [
                    'baseline_profile_id' => (int) $record->getKey(),
                    'name' => (string) $record->name,
                ]);

                Notification::make()
                    ->title('Baseline profile archived')
                    ->success()
                    ->send();
            });

        if ($workspace instanceof Workspace) {
            $action = WorkspaceUiEnforcement::forTableAction($action, $workspace)
                ->requireCapability(Capabilities::WORKSPACE_BASELINES_MANAGE)
                ->destructive()
                ->apply();
        }

        return $action;
    }
}
