<?php

declare(strict_types=1);

namespace App\Filament\Resources\BaselineProfileResource\RelationManagers;

use App\Models\BaselineProfile;
use App\Models\BaselineTenantAssignment;
use App\Models\ManagedEnvironment;
use App\Models\User;
use App\Models\Workspace;
use App\Services\Audit\WorkspaceAuditLogger;
use App\Support\Auth\Capabilities;
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 Filament\Actions\Action;
use Filament\Forms\Components\Select;
use Filament\Notifications\Notification;
use Filament\Resources\RelationManagers\RelationManager;
use Filament\Tables;
use Filament\Tables\Table;

class BaselineTenantAssignmentsRelationManager extends RelationManager
{
    protected static string $relationship = 'tenantAssignments';

    protected static ?string $title = 'Environment assignments';

    /**
     * @var array<int, array{baseline_profile_id:int, baseline_profile_name:string}>|null
     */
    protected ?array $tenantAssignmentSummaries = null;

    public static function actionSurfaceDeclaration(): ActionSurfaceDeclaration
    {
        return ActionSurfaceDeclaration::forRelationManager(ActionSurfaceProfile::RelationManager)
            ->satisfy(ActionSurfaceSlot::ListHeader, 'Header action: Assign environment (manage-gated).')
            ->satisfy(ActionSurfaceSlot::InspectAffordance, ActionSurfaceInspectAffordance::ClickableRow->value)
            ->exempt(ActionSurfaceSlot::ListRowMoreMenu, 'v1 assignments have no row-level actions beyond delete.')
            ->exempt(ActionSurfaceSlot::ListBulkMoreGroup, 'No bulk mutations for assignments in v1.')
            ->satisfy(ActionSurfaceSlot::ListEmptyState, 'Empty state encourages assigning an environment.');
    }

    public function table(Table $table): Table
    {
        return $table
            ->defaultSort('created_at', 'desc')
            ->paginated(\App\Support\Filament\TablePaginationProfiles::relationManager())
            ->columns([
                Tables\Columns\TextColumn::make('tenant.name')
                    ->label('Managed environment')
                    ->searchable(),
                Tables\Columns\TextColumn::make('assignedByUser.name')
                    ->label('Assigned by')
                    ->placeholder('—'),
                Tables\Columns\TextColumn::make('created_at')
                    ->label('Assigned at')
                    ->dateTime()
                    ->sortable(),
            ])
            ->headerActions([
                $this->assignTenantAction()
                    ->hidden(fn (): bool => $this->getOwnerRecord()->tenantAssignments()->doesntExist()),
            ])
            ->actions([
                $this->removeAssignmentAction(),
            ])
            ->emptyStateHeading('No environments assigned')
            ->emptyStateDescription('Assign an environment to compare its state against this baseline profile.')
            ->emptyStateActions([
                $this->assignTenantAction()->name('assignEmpty'),
            ]);
    }

    private function assignTenantAction(): Action
    {
        return Action::make('assign')
            ->label('Assign environment')
            ->icon('heroicon-o-plus')
            ->visible(fn (): bool => $this->hasManageCapability())
            ->form([
                Select::make('managed_environment_id')
                    ->label('Managed environment')
                    ->options(fn (): array => $this->getTenantOptions())
                    ->disableOptionWhen(fn (string $value): bool => array_key_exists((int) $value, $this->getTenantAssignmentSummaries()))
                    ->required()
                    ->searchable(),
            ])
            ->action(function (array $data): void {
                $user = auth()->user();

                if (! $user instanceof User || ! $this->hasManageCapability()) {
                    Notification::make()
                        ->title('Permission denied')
                        ->danger()
                        ->send();

                    return;
                }

                /** @var BaselineProfile $profile */
                $profile = $this->getOwnerRecord();
                $tenantId = (int) $data['managed_environment_id'];

                $existing = BaselineTenantAssignment::query()
                    ->where('workspace_id', $profile->workspace_id)
                    ->where('managed_environment_id', $tenantId)
                    ->first();

                if ($existing instanceof BaselineTenantAssignment) {
                    $assignedBaselineName = $this->getAssignedBaselineNameForTenant($tenantId);

                    Notification::make()
                        ->title('Environment already assigned')
                        ->body($assignedBaselineName === null
                            ? 'This environment already has a baseline assignment in this workspace.'
                            : "This environment is already assigned to baseline: {$assignedBaselineName}.")
                        ->warning()
                        ->send();

                    return;
                }

                $assignment = BaselineTenantAssignment::create([
                    'workspace_id' => (int) $profile->workspace_id,
                    'managed_environment_id' => $tenantId,
                    'baseline_profile_id' => (int) $profile->getKey(),
                    'assigned_by_user_id' => (int) $user->getKey(),
                ]);

                $this->auditAssignment($profile, $assignment, $user, 'created');

                Notification::make()
                    ->title('Environment assigned')
                    ->success()
                    ->send();

                $this->forgetTenantAssignmentSummaries();
            });
    }

    private function removeAssignmentAction(): Action
    {
        return Action::make('remove')
            ->label('Remove')
            ->icon('heroicon-o-trash')
            ->color('danger')
            ->visible(fn (): bool => $this->hasManageCapability())
            ->requiresConfirmation()
            ->modalHeading('Remove environment assignment')
            ->modalDescription('Are you sure you want to remove this environment assignment? This will not delete any existing findings.')
            ->action(function (BaselineTenantAssignment $record): void {
                $user = auth()->user();

                if (! $user instanceof User || ! $this->hasManageCapability()) {
                    Notification::make()
                        ->title('Permission denied')
                        ->danger()
                        ->send();

                    return;
                }

                /** @var BaselineProfile $profile */
                $profile = $this->getOwnerRecord();

                $this->auditAssignment($profile, $record, $user, 'removed');

                $record->delete();

                Notification::make()
                    ->title('Assignment removed')
                    ->success()
                    ->send();

                $this->forgetTenantAssignmentSummaries();
            });
    }

    /**
     * @return array<int, string>
     */
    private function getTenantOptions(): array
    {
        /** @var BaselineProfile $profile */
        $profile = $this->getOwnerRecord();

        $assignmentSummaries = $this->getTenantAssignmentSummaries();

        return ManagedEnvironment::query()
            ->where('workspace_id', $profile->workspace_id)
            ->orderBy('name')
            ->get(['id', 'name'])
            ->mapWithKeys(function (ManagedEnvironment $tenant) use ($assignmentSummaries): array {
                $tenantId = (int) $tenant->getKey();
                $assignmentSummary = $assignmentSummaries[$tenantId] ?? null;

                return [
                    $tenantId => $this->formatTenantOptionLabel($tenant, $assignmentSummary),
                ];
            })
            ->all();
    }

    /**
     * @return array<int, array{baseline_profile_id:int, baseline_profile_name:string}>
     */
    private function getTenantAssignmentSummaries(): array
    {
        if (is_array($this->tenantAssignmentSummaries)) {
            return $this->tenantAssignmentSummaries;
        }

        /** @var BaselineProfile $profile */
        $profile = $this->getOwnerRecord();

        $this->tenantAssignmentSummaries = BaselineTenantAssignment::query()
            ->where('workspace_id', (int) $profile->workspace_id)
            ->with('baselineProfile:id,name')
            ->get(['managed_environment_id', 'baseline_profile_id'])
            ->mapWithKeys(function (BaselineTenantAssignment $assignment): array {
                $baselineProfile = $assignment->baselineProfile;

                return [
                    (int) $assignment->managed_environment_id => [
                        'baseline_profile_id' => (int) $assignment->baseline_profile_id,
                        'baseline_profile_name' => $baselineProfile instanceof BaselineProfile
                            ? (string) $baselineProfile->name
                            : 'another baseline profile',
                    ],
                ];
            })
            ->all();

        return $this->tenantAssignmentSummaries;
    }

    /**
     * @param  array{baseline_profile_id:int, baseline_profile_name:string}|null  $assignmentSummary
     */
    private function formatTenantOptionLabel(
        ManagedEnvironment $tenant,
        ?array $assignmentSummary,
    ): string {
        $tenantName = (string) $tenant->name;

        if ($assignmentSummary === null) {
            return $tenantName;
        }

        return "{$tenantName} (assigned to baseline: {$assignmentSummary['baseline_profile_name']})";
    }

    private function getAssignedBaselineNameForTenant(int $tenantId): ?string
    {
        $assignmentSummary = $this->getTenantAssignmentSummaries()[$tenantId] ?? null;

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

        return $assignmentSummary['baseline_profile_name'];
    }

    private function forgetTenantAssignmentSummaries(): void
    {
        $this->tenantAssignmentSummaries = null;
    }

    private function auditAssignment(
        BaselineProfile $profile,
        BaselineTenantAssignment $assignment,
        User $user,
        string $action,
    ): void {
        $workspace = Workspace::query()->find($profile->workspace_id);

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

        $tenant = ManagedEnvironment::query()->find($assignment->managed_environment_id);

        $auditLogger = app(WorkspaceAuditLogger::class);

        $auditLogger->log(
            workspace: $workspace,
            action: 'baseline.assignment.'.$action,
            context: [
                'baseline_profile_id' => (int) $profile->getKey(),
                'baseline_profile_name' => (string) $profile->name,
                'managed_environment_id' => (int) $assignment->managed_environment_id,
                'tenant_name' => $tenant instanceof ManagedEnvironment ? (string) $tenant->display_name : '—',
            ],
            actor: $user,
            resourceType: 'baseline_profile',
            resourceId: (string) $profile->getKey(),
        );
    }

    private function hasManageCapability(): bool
    {
        $user = auth()->user();

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

        $workspaceId = app(WorkspaceContext::class)->currentWorkspaceId(request());

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

        $workspace = Workspace::query()->whereKey($workspaceId)->first();

        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_MANAGE);
    }
}
