<?php

declare(strict_types=1);

namespace App\Filament\Pages\Governance;

use App\Filament\Concerns\CleansAdminTenantQueryParameter;
use App\Filament\Concerns\ClearsWorkspaceHubEnvironmentFilterState;
use App\Filament\Resources\FindingExceptionResource;
use App\Models\FindingException;
use App\Models\ManagedEnvironment;
use App\Models\User;
use App\Models\Workspace;
use App\Services\Auth\CapabilityResolver;
use App\Services\Auth\WorkspaceCapabilityResolver;
use App\Support\Auth\Capabilities;
use App\Support\Badges\BadgeDomain;
use App\Support\Badges\BadgeRenderer;
use App\Support\Filament\TablePaginationProfiles;
use App\Support\GovernanceDecisions\GovernanceDecisionRegisterBuilder;
use App\Support\Navigation\CanonicalNavigationContext;
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\Workspaces\WorkspaceContext;
use BackedEnum;
use Filament\Actions\Action;
use Filament\Facades\Filament;
use Filament\Pages\Page;
use Filament\Tables;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Concerns\InteractsWithTable;
use Filament\Tables\Contracts\HasTable;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use UnitEnum;

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

    protected static bool $isDiscovered = false;

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

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

    protected static ?string $navigationLabel = 'Decision register';

    protected static ?int $navigationSort = 6;

    protected static ?string $title = 'Decision register';

    protected static ?string $slug = 'governance/decisions';

    protected string $view = 'filament.pages.governance.decision-register';

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

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

    /**
     * @var array<string, mixed>|null
     */
    private ?array $registerPayload = null;

    /**
     * @var array<string, mixed>|null
     */
    private ?array $unfilteredRegisterPayload = null;

    /**
     * @var array<int, array<string, mixed>>|null
     */
    private ?array $rowPayloadByExceptionId = null;

    private ?Workspace $workspace = null;

    public ?int $tenantId = null;

    public string $registerState = 'open';

    public static function actionSurfaceDeclaration(): ActionSurfaceDeclaration
    {
        return ActionSurfaceDeclaration::forPage(ActionSurfaceProfile::ListOnlyReadOnly, ActionSurfaceType::ReadOnlyRegistryReport)
            ->satisfy(ActionSurfaceSlot::ListHeader, 'Header controls keep tenant and register-state scope visible without introducing a second mutation surface.')
            ->satisfy(ActionSurfaceSlot::InspectAffordance, ActionSurfaceInspectAffordance::ClickableRow->value)
            ->exempt(ActionSurfaceSlot::ListRowMoreMenu, 'The decision register keeps one dominant row action and avoids a More menu in v1.')
            ->exempt(ActionSurfaceSlot::ListBulkMoreGroup, 'The decision register is read-only and intentionally omits bulk actions.')
            ->satisfy(ActionSurfaceSlot::ListEmptyState, 'Filtered empty states stay truthful and provide one path back to the broader register scope.')
            ->exempt(ActionSurfaceSlot::DetailHeader, 'The register owns no local detail surface; existing exception detail remains the action owner.');
    }

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

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

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

        $workspace = static::resolveWorkspaceFromRequest();

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

        $resolver = app(WorkspaceCapabilityResolver::class);

        if (! $resolver->isMember($user, $workspace)) {
            return false;
        }

        $visibleTenants = static::resolveVisibleDecisionTenantsFor($user, $workspace);

        return $visibleTenants !== [];
    }

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

        $this->mountInteractsWithTable();
        $this->resetWorkspaceHubEnvironmentFilterStateForCleanEntry(request());
        $this->authorizeWorkspaceMembership();
        $this->applyRequestedTenantPrefilter();
        $this->registerState = $this->resolveRequestedRegisterState();
        $this->ensureRegisterIsVisible();
    }

    public function pageUrl(array $overrides = []): string
    {
        $selectedTenant = $this->selectedTenant();
        $resolvedTenant = array_key_exists('environment_id', $overrides)
            ? $overrides['environment_id']
            : ($selectedTenant?->getKey() !== null ? (string) $selectedTenant->getKey() : null);
        $resolvedRegisterState = array_key_exists('register_state', $overrides)
            ? $overrides['register_state']
            : $this->registerState;

        return static::getUrl(
            panel: 'admin',
            parameters: array_filter([
                'environment_id' => (is_string($resolvedTenant) || is_numeric($resolvedTenant)) && (string) $resolvedTenant !== '' ? (string) $resolvedTenant : null,
                'register_state' => is_string($resolvedRegisterState) && $resolvedRegisterState !== 'open' ? $resolvedRegisterState : null,
            ], static fn (mixed $value): bool => $value !== null && $value !== ''),
        );
    }

    public function appliedScope(): array
    {
        return [
            'workspace_label' => $this->workspace()?->name,
            'tenant_label' => $this->selectedTenant()?->name,
            'register_state_label' => $this->registerStateLabel($this->registerState),
            'visible_count' => $this->registerPayload()['counts'][$this->registerState] ?? 0,
        ];
    }

    /**
     * @return list<array{key: string, label: string, count: int}>
     */
    public function availableRegisterStates(): array
    {
        $counts = $this->registerPayload()['counts'] ?? ['open' => 0, 'recently_closed' => 0];

        return [
            [
                'key' => 'open',
                'label' => 'Open decisions',
                'count' => (int) ($counts['open'] ?? 0),
            ],
            [
                'key' => 'recently_closed',
                'label' => 'Recently closed',
                'count' => (int) ($counts['recently_closed'] ?? 0),
            ],
        ];
    }

    public function hasTenantPrefilter(): bool
    {
        return $this->selectedTenant() instanceof ManagedEnvironment;
    }

    public function isActiveRegisterState(string $registerState): bool
    {
        return $this->registerState === $registerState;
    }

    public function emptyStateHeading(): string
    {
        if ($this->tenantFilterAloneExcludesRows()) {
            return 'This environment filter is hiding other visible decision follow-through';
        }

        if ($this->registerState === 'recently_closed') {
            return 'No recently closed decisions match this filter right now.';
        }

        return 'No open decisions match this filter right now.';
    }

    public function emptyStateDescription(): string
    {
        if ($this->tenantFilterAloneExcludesRows()) {
            return 'The current environment scope is calm, but other visible environments in this workspace still have open governance decisions.';
        }

        if ($this->registerState === 'recently_closed') {
            return 'Switch back to open decisions to continue the current follow-through lane, or widen the environment scope if you were filtering the register.';
        }

        return 'Try widening the environment scope or switch to recently closed decisions if you are checking what was just finished.';
    }

    public function emptyStateActionLabel(): ?string
    {
        if ($this->tenantFilterAloneExcludesRows()) {
            return 'Clear environment filter';
        }

        if ($this->registerState === 'recently_closed') {
            return 'Open current decisions';
        }

        return null;
    }

    public function emptyStateActionUrl(): ?string
    {
        if ($this->tenantFilterAloneExcludesRows()) {
            return $this->pageUrl(['environment_id' => null, 'register_state' => 'open']);
        }

        if ($this->registerState === 'recently_closed') {
            return $this->pageUrl(['register_state' => 'open']);
        }

        return null;
    }

    public function table(Table $table): Table
    {
        return $table
            ->query($this->tableQuery())
            ->defaultSort('review_due_at', 'asc')
            ->paginated(TablePaginationProfiles::resource())
            ->persistFiltersInSession()
            ->persistSearchInSession()
            ->persistSortInSession()
            ->recordUrl(fn (FindingException $record): ?string => $this->decisionUrl($record))
            ->columns([
                TextColumn::make('tenant.name')
                    ->label('Environment')
                    ->searchable()
                    ->sortable(),
                TextColumn::make('status')
                    ->badge()
                    ->formatStateUsing(BadgeRenderer::label(BadgeDomain::FindingExceptionStatus))
                    ->color(BadgeRenderer::color(BadgeDomain::FindingExceptionStatus))
                    ->icon(BadgeRenderer::icon(BadgeDomain::FindingExceptionStatus))
                    ->iconColor(BadgeRenderer::iconColor(BadgeDomain::FindingExceptionStatus))
                    ->sortable(),
                TextColumn::make('current_validity_state')
                    ->label('Impact')
                    ->badge()
                    ->formatStateUsing(BadgeRenderer::label(BadgeDomain::FindingRiskGovernanceValidity))
                    ->color(BadgeRenderer::color(BadgeDomain::FindingRiskGovernanceValidity))
                    ->icon(BadgeRenderer::icon(BadgeDomain::FindingRiskGovernanceValidity))
                    ->iconColor(BadgeRenderer::iconColor(BadgeDomain::FindingRiskGovernanceValidity)),
                TextColumn::make('owner.name')
                    ->label('Owner')
                    ->placeholder('—')
                    ->toggleable(),
                TextColumn::make('review_due_at')
                    ->label('Review due')
                    ->dateTime()
                    ->since()
                    ->placeholder('—')
                    ->tooltip(fn (FindingException $record): ?string => $record->review_due_at?->toDayDateTimeString())
                    ->sortable(),
                TextColumn::make('proof_availability')
                    ->label('Proof')
                    ->state(fn (FindingException $record): string => (string) ($this->rowPayload($record)['proof_label'] ?? 'No linked proof'))
                    ->description(fn (FindingException $record): ?string => $this->proofUrl($record) !== null
                        ? (string) ($this->rowPayload($record)['proof_url_label'] ?? 'View proof')
                        : null)
                    ->url(fn (FindingException $record): ?string => $this->proofUrl($record))
                    ->wrap(),
                TextColumn::make('operation_run_link')
                    ->label('Operation')
                    ->state(fn (FindingException $record): string => (string) ($this->rowPayload($record)['operation_run_label'] ?? 'No operation linked'))
                    ->url(fn (FindingException $record): ?string => $this->operationRunUrl($record))
                    ->wrap(),
                TextColumn::make('next_action_label')
                    ->label('Next action')
                    ->state(fn (FindingException $record): ?string => $this->rowPayload($record)['next_action_label'] ?? null)
                    ->visible(fn (): bool => $this->registerState === 'open')
                    ->wrap(),
                TextColumn::make('closure_reason')
                    ->label('Closure reason')
                    ->state(fn (FindingException $record): ?string => $this->rowPayload($record)['closure_reason'] ?? null)
                    ->placeholder('—')
                    ->visible(fn (): bool => $this->registerState === 'recently_closed')
                    ->wrap(),
            ])
            ->emptyStateHeading($this->emptyStateHeading())
            ->emptyStateDescription($this->emptyStateDescription())
            ->emptyStateActions($this->emptyStateActions());
    }

    /**
     * @return list<Tables\Actions\Action>
     */
    private function emptyStateActions(): array
    {
        $label = $this->emptyStateActionLabel();
        $url = $this->emptyStateActionUrl();

        if (! is_string($label) || ! is_string($url)) {
            return [];
        }

        return [
            Action::make('empty_state_scope_action')
                ->label($label)
                ->url($url),
        ];
    }

    /**
     * @return Builder<FindingException>
     */
    private function tableQuery(): Builder
    {
        $tenantIds = array_values(array_map(
            static fn (ManagedEnvironment $tenant): int => (int) $tenant->getKey(),
            $this->currentScopeTenants(),
        ));

        $query = FindingException::query()
            ->where('workspace_id', (int) $this->workspace()?->getKey())
            ->whereIn('managed_environment_id', $tenantIds)
            ->with(['tenant', 'owner', 'currentDecision']);

        if ($this->registerState === 'recently_closed') {
            return $query
                ->whereIn('status', [
                    FindingException::STATUS_REJECTED,
                    FindingException::STATUS_REVOKED,
                    FindingException::STATUS_SUPERSEDED,
                ])
                ->whereHas('currentDecision', function (Builder $decisionQuery): void {
                    $decisionQuery->where('decided_at', '>=', now()->startOfDay()->subDays(30));
                });
        }

        return $query
            ->whereNotIn('status', [
                FindingException::STATUS_REJECTED,
                FindingException::STATUS_REVOKED,
                FindingException::STATUS_SUPERSEDED,
            ]);
    }

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

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

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

        $resolver = app(WorkspaceCapabilityResolver::class);

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

    private function ensureRegisterIsVisible(): void
    {
        if ($this->visibleDecisionTenants() === []) {
            abort(403);
        }

        if ($this->tenantId !== null || $this->registerState !== 'open') {
            return;
        }

        $counts = $this->unfilteredRegisterPayload()['counts'] ?? [];

        if ((int) ($counts['open'] ?? 0) > 0) {
            return;
        }

        if ((int) ($counts['recently_closed'] ?? 0) > 0) {
            $this->redirect($this->pageUrl(['register_state' => 'recently_closed']), navigate: true);

            return;
        }

        // A clean workspace register URL is a valid workspace hub entry, even when the
        // current register has no rows. The table empty state owns that operator truth.
    }

    /**
     * @return array<int, ManagedEnvironment>
     */
    private 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 = static::resolveAuthorizedTenantsFor($user, $workspace);
    }

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

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

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

        return $this->visibleDecisionTenants = static::resolveVisibleDecisionTenantsFor($user, $workspace, $tenants);
    }

    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->visibleDecisionTenants() as $tenant) {
            if ((int) $tenant->getKey() === $environmentId) {
                $this->tenantId = $environmentId;

                return;
            }
        }

        throw new NotFoundHttpException;
    }

    private function resolveRequestedRegisterState(): string
    {
        $registerState = request()->query('register_state');

        if (! is_string($registerState)) {
            return 'open';
        }

        return in_array($registerState, ['open', 'recently_closed'], true)
            ? $registerState
            : 'open';
    }

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

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

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

    /**
     * @return array<int, ManagedEnvironment>
     */
    private static function resolveAuthorizedTenantsFor(User $user, Workspace $workspace): array
    {
        return $user->accessibleManagedEnvironmentsQuery((int) $workspace->getKey())
            ->where('managed_environments.lifecycle_status', 'active')
            ->orderBy('managed_environments.name')
            ->get(['managed_environments.id', 'managed_environments.name', 'managed_environments.slug', 'managed_environments.workspace_id'])
            ->all();
    }

    /**
     * @param  array<int, ManagedEnvironment>|null  $authorizedTenants
     * @return array<int, ManagedEnvironment>
     */
    private static function resolveVisibleDecisionTenantsFor(User $user, Workspace $workspace, ?array $authorizedTenants = null): array
    {
        $tenants = $authorizedTenants ?? static::resolveAuthorizedTenantsFor($user, $workspace);

        if ($tenants === []) {
            return [];
        }

        $resolver = app(CapabilityResolver::class);
        $resolver->primeMemberships(
            $user,
            array_map(static fn (ManagedEnvironment $tenant): int => (int) $tenant->getKey(), $tenants),
        );

        return array_values(array_filter(
            $tenants,
            fn (ManagedEnvironment $tenant): bool => $resolver->can($user, $tenant, Capabilities::FINDING_EXCEPTION_VIEW),
        ));
    }

    private function workspace(): ?Workspace
    {
        if ($this->workspace instanceof Workspace) {
            return $this->workspace;
        }

        return $this->workspace = static::resolveWorkspaceFromRequest();
    }

    private function selectedTenant(): ?ManagedEnvironment
    {
        if (! is_int($this->tenantId)) {
            return null;
        }

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

        return null;
    }

    /**
     * @return array<int, ManagedEnvironment>
     */
    private function currentScopeTenants(): array
    {
        $selectedTenant = $this->selectedTenant();

        if ($selectedTenant instanceof ManagedEnvironment) {
            return [$selectedTenant];
        }

        return $this->visibleDecisionTenants();
    }

    /**
     * @return array<string, mixed>
     */
    private function registerPayload(): array
    {
        if (is_array($this->registerPayload)) {
            return $this->registerPayload;
        }

        $workspace = $this->workspace();

        if (! $workspace instanceof Workspace) {
            return $this->registerPayload = [
                'rows' => [],
                'counts' => ['open' => 0, 'recently_closed' => 0],
            ];
        }

        return $this->registerPayload = app(GovernanceDecisionRegisterBuilder::class)->build(
            workspace: $workspace,
            visibleTenants: $this->currentScopeTenants(),
            registerState: $this->registerState,
        );
    }

    /**
     * @return array<string, mixed>
     */
    private function unfilteredRegisterPayload(): array
    {
        if (is_array($this->unfilteredRegisterPayload)) {
            return $this->unfilteredRegisterPayload;
        }

        $workspace = $this->workspace();

        if (! $workspace instanceof Workspace) {
            return $this->unfilteredRegisterPayload = [
                'rows' => [],
                'counts' => ['open' => 0, 'recently_closed' => 0],
            ];
        }

        return $this->unfilteredRegisterPayload = app(GovernanceDecisionRegisterBuilder::class)->build(
            workspace: $workspace,
            visibleTenants: $this->visibleDecisionTenants(),
            registerState: 'open',
        );
    }

    /**
     * @return array<string, mixed>
     */
    private function rowPayload(FindingException $record): array
    {
        if (! is_array($this->rowPayloadByExceptionId)) {
            $this->rowPayloadByExceptionId = collect($this->registerPayload()['rows'] ?? [])
                ->keyBy('exception_id')
                ->all();
        }

        return $this->rowPayloadByExceptionId[(int) $record->getKey()] ?? [];
    }

    private function tenantFilterAloneExcludesRows(): bool
    {
        if (! is_int($this->tenantId) || $this->registerState !== 'open') {
            return false;
        }

        if (($this->registerPayload()['rows'] ?? []) !== []) {
            return false;
        }

        return (int) ($this->unfilteredRegisterPayload()['counts']['open'] ?? 0) > 0;
    }

    private function registerStateLabel(string $registerState): string
    {
        return match ($registerState) {
            'recently_closed' => 'Recently closed',
            default => 'Open decisions',
        };
    }

    public function decisionUrl(FindingException $record): ?string
    {
        $tenant = $record->tenant;

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

        return $this->appendQuery(
            FindingExceptionResource::getUrl('view', ['record' => $record], tenant: $tenant),
            $this->navigationContext()->toQuery(),
        );
    }

    private function proofUrl(FindingException $record): ?string
    {
        $row = $this->rowPayload($record);
        $proofState = $row['proof_state'] ?? null;

        if ($proofState === 'linked_detail_section') {
            return $this->decisionUrl($record);
        }

        $url = $row['proof_url'] ?? null;

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

    private function operationRunUrl(FindingException $record): ?string
    {
        $url = $this->rowPayload($record)['operation_run_url'] ?? null;

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

    private function navigationContext(): CanonicalNavigationContext
    {
        return CanonicalNavigationContext::forDecisionRegister(
            canonicalRouteName: static::getRouteName(Filament::getPanel('admin')),
            tenantId: $this->tenantId,
            backLinkUrl: $this->pageUrl(),
        );
    }

    /**
     * @param  array<string, mixed>  $query
     */
    private function appendQuery(string $url, array $query): string
    {
        $queryString = http_build_query($query);

        if ($queryString === '') {
            return $url;
        }

        $separator = str_contains($url, '?') ? '&' : '?';

        return $url.$separator.$queryString;
    }
}
