<?php

declare(strict_types=1);

namespace App\Filament\Pages\Reviews;

use App\Filament\Concerns\CleansAdminTenantQueryParameter;
use App\Filament\Concerns\ClearsWorkspaceHubEnvironmentFilterState;
use App\Filament\Resources\EnvironmentReviewResource;
use App\Models\EnvironmentReview;
use App\Models\ManagedEnvironment;
use App\Models\User;
use App\Models\Workspace;
use App\Services\EnvironmentReviews\EnvironmentReviewRegisterService;
use App\Services\ReviewPackService;
use App\Support\Auth\Capabilities;
use App\Support\Badges\BadgeCatalog;
use App\Support\Badges\BadgeDomain;
use App\Support\Badges\BadgeRenderer;
use App\Support\EnvironmentReviewCompletenessState;
use App\Support\Filament\FilterPresets;
use App\Support\Filament\TablePaginationProfiles;
use App\Support\Findings\FindingOutcomeSemantics;
use App\Support\Navigation\WorkspaceHubEnvironmentFilter;
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\ActionSurface\Enums\ActionSurfaceType;
use App\Support\Ui\GovernanceArtifactTruth\ArtifactTruthEnvelope;
use App\Support\Ui\GovernanceArtifactTruth\ArtifactTruthPresenter;
use App\Support\Ui\GovernanceArtifactTruth\CompressedGovernanceOutcome;
use App\Support\Ui\GovernanceArtifactTruth\SurfaceCompressionContext;
use App\Support\Workspaces\WorkspaceContext;
use BackedEnum;
use Filament\Actions\Action;
use Filament\Pages\Page;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Concerns\InteractsWithTable;
use Filament\Tables\Contracts\HasTable;
use Filament\Tables\Filters\SelectFilter;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use UnitEnum;

class ReviewRegister extends Page implements HasTable
{
    use CleansAdminTenantQueryParameter;
    use ClearsWorkspaceHubEnvironmentFilterState;
    use InteractsWithTable;

    protected static bool $isDiscovered = false;

    protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-document-magnifying-glass';

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

    protected static ?string $navigationLabel = 'Reviews';

    protected static ?string $title = 'Review Register';

    protected static ?string $slug = 'reviews';

    protected string $view = 'filament.pages.reviews.review-register';

    /**
     * @var array<int, ManagedEnvironment>|null
     */
    private ?array $authorizedTenants = null;

    public static function actionSurfaceDeclaration(): ActionSurfaceDeclaration
    {
        return ActionSurfaceDeclaration::forPage(ActionSurfaceProfile::RunLog, ActionSurfaceType::ReadOnlyRegistryReport)
            ->satisfy(ActionSurfaceSlot::ListHeader, 'Header actions provide a single Clear filters action for the canonical review register.')
            ->satisfy(ActionSurfaceSlot::InspectAffordance, ActionSurfaceInspectAffordance::ClickableRow->value)
            ->exempt(ActionSurfaceSlot::ListBulkMoreGroup, 'The review register does not expose bulk actions in the first slice.')
            ->satisfy(ActionSurfaceSlot::ListEmptyState, 'The empty state keeps exactly one Clear filters CTA.')
            ->exempt(ActionSurfaceSlot::DetailHeader, 'Row navigation returns to the tenant-scoped review detail rather than opening an inline canonical detail panel.');
    }

    public function mount(): void
    {
        $this->authorizePageAccess();

        $this->resetWorkspaceHubEnvironmentFilterStateForCleanEntry(request());

        $this->applyRequestedTenantPrefilter();
        $this->mountInteractsWithTable();
        $this->resetWorkspaceHubEnvironmentFilterStateForCleanEntry(request());
    }

    protected function getHeaderActions(): array
    {
        return [
            Action::make('clear_filters')
                ->label('Clear filters')
                ->icon('heroicon-o-x-mark')
                ->color('gray')
                ->visible(fn (): bool => $this->hasActiveFilters())
                ->action(function (): void {
                    $this->clearRegisterFilters();
                }),
        ];
    }

    public function table(Table $table): Table
    {
        return $table
            ->query(fn (): Builder => $this->registerQuery())
            ->defaultSort('generated_at', 'desc')
            ->paginated(TablePaginationProfiles::customPage())
            ->persistFiltersInSession()
            ->persistSearchInSession()
            ->persistSortInSession()
            ->recordUrl(fn (EnvironmentReview $record): string => EnvironmentReviewResource::environmentScopedUrl('view', ['record' => $record], $record->tenant))
            ->columns([
                TextColumn::make('tenant.name')->label('Environment')->searchable(),
                TextColumn::make('status')
                    ->badge()
                    ->formatStateUsing(BadgeRenderer::label(BadgeDomain::EnvironmentReviewStatus))
                    ->color(BadgeRenderer::color(BadgeDomain::EnvironmentReviewStatus))
                    ->icon(BadgeRenderer::icon(BadgeDomain::EnvironmentReviewStatus))
                    ->iconColor(BadgeRenderer::iconColor(BadgeDomain::EnvironmentReviewStatus)),
                TextColumn::make('outcome')
                    ->label('Outcome')
                    ->badge()
                    ->getStateUsing(\Closure::fromCallable([$this, 'reviewOutcomeLabel']))
                    ->color(\Closure::fromCallable([$this, 'reviewOutcomeBadgeColor']))
                    ->icon(\Closure::fromCallable([$this, 'reviewOutcomeBadgeIcon']))
                    ->iconColor(\Closure::fromCallable([$this, 'reviewOutcomeBadgeIconColor']))
                    ->description(\Closure::fromCallable([$this, 'reviewOutcomeDescription']))
                    ->wrap(),
                TextColumn::make('generated_at')->dateTime()->placeholder('—')->sortable(),
                TextColumn::make('published_at')->dateTime()->placeholder('—')->sortable(),
                TextColumn::make('next_step')
                    ->label('Next step')
                    ->getStateUsing(\Closure::fromCallable([$this, 'reviewOutcomeNextStep']))
                    ->wrap(),
            ])
            ->filters([
                SelectFilter::make('managed_environment_id')
                    ->label('Environment')
                    ->options(fn (): array => $this->tenantFilterOptions())
                    ->default(fn (): ?string => $this->defaultTenantFilter())
                    ->searchable(),
                SelectFilter::make('status')
                    ->options([
                        'draft' => 'Draft',
                        'ready' => 'Ready',
                        'published' => 'Published',
                        'archived' => 'Archived',
                        'superseded' => 'Superseded',
                        'failed' => 'Failed',
                    ]),
                SelectFilter::make('completeness_state')
                    ->label('Completeness')
                    ->options(BadgeCatalog::options(BadgeDomain::EnvironmentReviewCompleteness, EnvironmentReviewCompletenessState::values())),
                SelectFilter::make('published_state')
                    ->label('Published state')
                    ->options([
                        'published' => 'Published',
                        'unpublished' => 'Not published',
                    ])
                    ->query(function (Builder $query, array $data): Builder {
                        return match ($data['value'] ?? null) {
                            'published' => $query->whereNotNull('published_at'),
                            'unpublished' => $query->whereNull('published_at'),
                            default => $query,
                        };
                    }),
                FilterPresets::dateRange('review_date', 'Review date', 'generated_at'),
            ])
            ->actions([
                Action::make('export_executive_pack')
                    ->label('Export executive pack')
                    ->icon('heroicon-o-arrow-down-tray')
                    ->visible(fn (EnvironmentReview $record): bool => auth()->user() instanceof User
                        && auth()->user()->can(Capabilities::ENVIRONMENT_REVIEW_MANAGE, $record->tenant)
                        && in_array($record->status, ['ready', 'published'], true))
                    ->disabled(fn (EnvironmentReview $record): bool => (bool) (app(ReviewPackService::class)->reviewPackGenerationDecisionForTenant($record->tenant)['is_blocked'] ?? false))
                    ->tooltip(function (EnvironmentReview $record): ?string {
                        $decision = app(ReviewPackService::class)->reviewPackGenerationDecisionForTenant($record->tenant);

                        if ((bool) ($decision['is_blocked'] ?? false)) {
                            $reason = $decision['block_reason'] ?? null;

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

                        if ((bool) ($decision['is_warning'] ?? false)) {
                            $reason = $decision['warning_reason'] ?? null;

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

                        return null;
                    })
                    ->action(fn (EnvironmentReview $record): mixed => EnvironmentReviewResource::executeExport($record)),
            ])
            ->bulkActions([])
            ->emptyStateHeading('No review records match this view')
            ->emptyStateDescription('Clear the current filters to return to the full review register for your entitled tenants.')
            ->emptyStateActions([
                Action::make('clear_filters_empty')
                    ->label('Clear filters')
                    ->icon('heroicon-o-x-mark')
                    ->color('gray')
                    ->action(fn (): mixed => $this->clearRegisterFilters()),
            ]);
    }

    /**
     * @return array<int, ManagedEnvironment>
     */
    public function authorizedTenants(): array
    {
        if ($this->authorizedTenants !== null) {
            return $this->authorizedTenants;
        }

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

        if (! $user instanceof User || ! $workspace instanceof Workspace) {
            return $this->authorizedTenants = [];
        }

        return $this->authorizedTenants = app(EnvironmentReviewRegisterService::class)->authorizedTenants($user, $workspace);
    }

    /**
     * @return array{label: string, clear_url: string}|null
     */
    public function environmentFilterChip(): ?array
    {
        $tenant = $this->filteredTenant();

        if (! $tenant instanceof ManagedEnvironment) {
            return null;
        }

        return [
            'label' => (string) $tenant->name,
            'clear_url' => $this->cleanWorkspaceHubUrl(static::getUrl(panel: 'admin')),
        ];
    }

    private function authorizePageAccess(): void
    {
        $user = auth()->user();
        $workspace = $this->workspace();

        if (! $user instanceof User) {
            abort(403);
        }

        if (! $workspace instanceof Workspace) {
            throw new NotFoundHttpException;
        }

        $service = app(EnvironmentReviewRegisterService::class);

        if (! $service->canAccessWorkspace($user, $workspace)) {
            throw new NotFoundHttpException;
        }

        if ($this->authorizedTenants() === []) {
            throw new NotFoundHttpException;
        }
    }

    private function registerQuery(): Builder
    {
        $user = auth()->user();
        $workspace = $this->workspace();

        if (! $user instanceof User || ! $workspace instanceof Workspace) {
            return EnvironmentReview::query()->whereRaw('1 = 0');
        }

        return app(EnvironmentReviewRegisterService::class)->query($user, $workspace);
    }

    /**
     * @return array<string, string>
     */
    private function tenantFilterOptions(): array
    {
        return collect($this->authorizedTenants())
            ->mapWithKeys(static fn (ManagedEnvironment $tenant): array => [
                (string) $tenant->getKey() => $tenant->name,
            ])
            ->all();
    }

    private function defaultTenantFilter(): ?string
    {
        return null;
    }

    private function applyRequestedTenantPrefilter(): void
    {
        $workspace = $this->workspace();

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

        $filter = WorkspaceHubEnvironmentFilter::fromRequest(request(), $workspace);

        if (! $filter instanceof WorkspaceHubEnvironmentFilter) {
            return;
        }

        $environmentId = $filter->environmentId();

        foreach ($this->authorizedTenants() as $tenant) {
            if ((int) $tenant->getKey() === $environmentId) {
                $this->tableFilters['managed_environment_id']['value'] = (string) $environmentId;
                $this->tableDeferredFilters['managed_environment_id']['value'] = (string) $environmentId;

                return;
            }
        }

        throw new NotFoundHttpException;
    }

    private function hasActiveFilters(): bool
    {
        return $this->currentTenantFilterId() !== null
            || is_string(data_get($this->tableFilters, 'status.value'))
            || is_string(data_get($this->tableFilters, 'completeness_state.value'))
            || is_string(data_get($this->tableFilters, 'published_state.value'))
            || filled(data_get($this->tableFilters, 'review_date.from'))
            || filled(data_get($this->tableFilters, 'review_date.until'));
    }

    private function clearRegisterFilters(): void
    {
        $hadEnvironmentFilter = $this->currentTenantFilterId() !== null;

        $this->removeTableFilters();
        $this->clearWorkspaceHubEnvironmentFilterState(request());

        if ($hadEnvironmentFilter) {
            $this->redirectToCleanWorkspaceHubUrl(static::getUrl(panel: 'admin'), request());
        }
    }

    private function currentTenantFilterId(): ?int
    {
        $tenantFilter = data_get($this->tableFilters, 'managed_environment_id.value');

        if (! is_numeric($tenantFilter)) {
            $tenantFilter = data_get(session()->get($this->getTableFiltersSessionKey(), []), 'managed_environment_id.value');
        }

        return is_numeric($tenantFilter) ? (int) $tenantFilter : null;
    }

    private function filteredTenant(): ?ManagedEnvironment
    {
        $tenantId = $this->currentTenantFilterId();

        if (! is_int($tenantId)) {
            return null;
        }

        foreach ($this->authorizedTenants() as $tenant) {
            if ((int) $tenant->getKey() === $tenantId) {
                return $tenant;
            }
        }

        return null;
    }

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

        return is_numeric($workspaceId)
            ? Workspace::query()->whereKey((int) $workspaceId)->first()
            : null;
    }

    private function reviewTruth(EnvironmentReview $record, bool $fresh = false): ArtifactTruthEnvelope
    {
        $presenter = app(ArtifactTruthPresenter::class);

        return $fresh
            ? $presenter->forEnvironmentReviewFresh($record)
            : $presenter->forEnvironmentReview($record);
    }

    private function reviewOutcomeLabel(EnvironmentReview $record): string
    {
        return $this->reviewOutcome($record)->primaryLabel;
    }

    private function reviewOutcomeBadgeColor(EnvironmentReview $record): string
    {
        return $this->reviewOutcome($record)->primaryBadge->color;
    }

    private function reviewOutcomeBadgeIcon(EnvironmentReview $record): ?string
    {
        return $this->reviewOutcome($record)->primaryBadge->icon;
    }

    private function reviewOutcomeBadgeIconColor(EnvironmentReview $record): ?string
    {
        return $this->reviewOutcome($record)->primaryBadge->iconColor;
    }

    private function reviewOutcomeDescription(EnvironmentReview $record): ?string
    {
        $primaryReason = $this->reviewOutcome($record)->primaryReason;
        $findingOutcomeSummary = $this->findingOutcomeSummary($record);

        if ($findingOutcomeSummary === null) {
            return $primaryReason;
        }

        return trim($primaryReason.' Terminal outcomes: '.$findingOutcomeSummary.'.');
    }

    private function reviewOutcomeNextStep(EnvironmentReview $record): string
    {
        return $this->reviewOutcome($record)->nextActionText;
    }

    private function reviewOutcome(EnvironmentReview $record, bool $fresh = false): CompressedGovernanceOutcome
    {
        $presenter = app(ArtifactTruthPresenter::class);
        $truth = $fresh
            ? $this->reviewTruth($record, true)
            : $this->reviewTruth($record);

        return $presenter->compressedOutcomeFor($record, SurfaceCompressionContext::reviewRegister(), $fresh)
            ?? $presenter->compressedOutcomeFromEnvelope(
                $truth,
                SurfaceCompressionContext::reviewRegister(),
            );
    }

    private function findingOutcomeSummary(EnvironmentReview $record): ?string
    {
        $summary = is_array($record->summary) ? $record->summary : [];
        $outcomeCounts = $summary['finding_outcomes'] ?? [];

        if (! is_array($outcomeCounts)) {
            return null;
        }

        return app(FindingOutcomeSemantics::class)->compactOutcomeSummary($outcomeCounts);
    }
}
