<?php

namespace App\Support\Middleware;

use App\Filament\Pages\WorkspaceOverview;
use App\Filament\Resources\AlertDeliveryResource;
use App\Filament\Resources\AlertDestinationResource;
use App\Filament\Resources\AlertRuleResource;
use App\Models\Tenant;
use App\Models\User;
use App\Models\Workspace;
use App\Models\WorkspaceMembership;
use App\Services\Auth\WorkspaceRoleCapabilityMap;
use App\Support\Auth\Capabilities;
use App\Support\Workspaces\WorkspaceContext;
use Closure;
use Filament\Facades\Filament;
use Filament\Models\Contracts\HasTenants;
use Filament\Navigation\NavigationBuilder;
use Filament\Navigation\NavigationItem;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class EnsureFilamentTenantSelected
{
    /**
     * @param  Closure(Request): Response  $next
     */
    public function handle(Request $request, Closure $next): Response
    {
        $panel = Filament::getCurrentOrDefaultPanel();

        $path = '/'.ltrim($request->path(), '/');

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

        $existingTenant = Filament::getTenant();
        if ($existingTenant instanceof Tenant && $workspaceId !== null && (int) $existingTenant->workspace_id !== (int) $workspaceId) {
            Filament::setTenant(null, true);
            $existingTenant = null;
        }

        $user = $request->user();
        if ($existingTenant instanceof Tenant && $user instanceof User && ! $user->canAccessTenant($existingTenant)) {
            Filament::setTenant(null, true);
        }

        if ($path === '/livewire/update') {
            $refererPath = parse_url((string) $request->headers->get('referer', ''), PHP_URL_PATH) ?? '';
            $refererPath = '/'.ltrim((string) $refererPath, '/');

            if (preg_match('#^/admin/operations/[^/]+$#', $refererPath) === 1) {
                $this->configureNavigationForRequest($panel);

                return $next($request);
            }

            if ($this->isWorkspaceScopedPageWithTenant($refererPath)) {
                $this->configureNavigationForRequest($panel);

                return $next($request);
            }
        }

        if (preg_match('#^/admin/operations/[^/]+$#', $path) === 1) {
            $this->configureNavigationForRequest($panel);

            return $next($request);
        }

        if ($path === '/admin/operations') {
            $this->configureNavigationForRequest($panel);

            return $next($request);
        }

        $tenantParameter = null;
        if ($request->route()?->hasParameter('tenant')) {
            $tenantParameter = $request->route()->parameter('tenant');
        } elseif (filled($request->query('tenant'))) {
            $tenantParameter = $request->query('tenant');
        }

        if (
            $tenantParameter === null
            && ! filled(Filament::getTenant())
            && $this->adminPathRequiresTenantSelection($path)
        ) {
            return redirect()->route('filament.admin.pages.choose-tenant');
        }

        if ($tenantParameter !== null) {
            $user = $request->user();

            if ($user === null) {
                return $next($request);
            }

            if (! $user instanceof HasTenants) {
                abort(404);
            }

            $tenant = $tenantParameter instanceof Tenant
                ? $tenantParameter
                : Tenant::query()->withTrashed()->where('external_id', (string) $tenantParameter)->first();

            if (! $tenant instanceof Tenant) {
                abort(404);
            }

            if ($workspaceId === null) {
                abort(404);
            }

            if ((int) $tenant->workspace_id !== (int) $workspaceId) {
                abort(404);
            }

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

            if (! $workspace instanceof Workspace) {
                abort(404);
            }

            if (! $user instanceof User || ! $workspaceContext->isMember($user, $workspace)) {
                abort(404);
            }

            if (! $user->canAccessTenant($tenant)) {
                abort(404);
            }

            if ($this->isWorkspaceScopedPageWithTenant($path)) {
                $this->configureNavigationForRequest($panel);

                return $next($request);
            }

            Filament::setTenant($tenant, true);

            app(WorkspaceContext::class)->rememberLastTenantId((int) $workspaceId, (int) $tenant->getKey(), $request);

            $this->configureNavigationForRequest($panel);

            return $next($request);
        }

        if (
            str_starts_with($path, '/admin/w/')
            || str_starts_with($path, '/admin/workspaces')
            || str_starts_with($path, '/admin/operations')
            || in_array($path, ['/admin', '/admin/choose-workspace', '/admin/choose-tenant', '/admin/no-access', '/admin/alerts', '/admin/audit-log', '/admin/onboarding', '/admin/settings/workspace'], true)
        ) {
            $this->configureNavigationForRequest($panel);

            return $next($request);
        }

        if (filled(Filament::getTenant())) {
            $this->configureNavigationForRequest($panel);

            return $next($request);
        }

        if (! $user instanceof User) {
            $this->configureNavigationForRequest($panel);

            return $next($request);
        }

        $this->configureNavigationForRequest($panel);

        return $next($request);
    }

    private function configureNavigationForRequest(\Filament\Panel $panel): void
    {
        if (! $panel->hasTenancy()) {
            return;
        }

        if (filled(Filament::getTenant())) {
            $panel->navigation(true);

            return;
        }

        $panel->navigation(function (): NavigationBuilder {
            return app(NavigationBuilder::class)
                ->item(WorkspaceOverview::navigationItem())
                ->item(
                    NavigationItem::make('Manage workspaces')
                        ->url(fn (): string => route('filament.admin.resources.workspaces.index'))
                        ->icon('heroicon-o-squares-2x2')
                        ->group('Settings')
                        ->sort(10)
                        ->visible(function (): bool {
                            $user = auth()->user();

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

                            $roles = WorkspaceRoleCapabilityMap::rolesWithCapability(Capabilities::WORKSPACE_MEMBERSHIP_MANAGE);

                            return WorkspaceMembership::query()
                                ->where('user_id', (int) $user->getKey())
                                ->whereIn('role', $roles)
                                ->exists();
                        }),
                )
                ->item(
                    NavigationItem::make('Operations')
                        ->url(fn (): string => route('admin.operations.index'))
                        ->icon('heroicon-o-queue-list')
                        ->group('Monitoring')
                        ->sort(10),
                )
                ->item(
                    NavigationItem::make('Alert targets')
                        ->url(fn (): string => AlertDestinationResource::getUrl(panel: 'admin'))
                        ->icon('heroicon-o-bell-alert')
                        ->group('Monitoring')
                        ->sort(20)
                        ->visible(fn (): bool => AlertDestinationResource::canViewAny()),
                )
                ->item(
                    NavigationItem::make('Alert rules')
                        ->url(fn (): string => AlertRuleResource::getUrl(panel: 'admin'))
                        ->icon('heroicon-o-funnel')
                        ->group('Monitoring')
                        ->sort(21)
                        ->visible(fn (): bool => AlertRuleResource::canViewAny()),
                )
                ->item(
                    NavigationItem::make('Alert deliveries')
                        ->url(fn (): string => AlertDeliveryResource::getUrl(panel: 'admin'))
                        ->icon('heroicon-o-clock')
                        ->group('Monitoring')
                        ->sort(22)
                        ->visible(fn (): bool => AlertDeliveryResource::canViewAny()),
                )
                ->item(
                    NavigationItem::make('Alerts')
                        ->url(fn (): string => '/admin/alerts')
                        ->icon('heroicon-o-bell-alert')
                        ->group('Monitoring')
                        ->sort(23),
                )
                ->item(
                    NavigationItem::make('Audit Log')
                        ->url(fn (): string => '/admin/audit-log')
                        ->icon('heroicon-o-clipboard-document-list')
                        ->group('Monitoring')
                        ->sort(30),
                );
        });
    }

    private function isWorkspaceScopedPageWithTenant(string $path): bool
    {
        return preg_match('#^/admin/tenants/[^/]+/required-permissions$#', $path) === 1;
    }

    private function adminPathRequiresTenantSelection(string $path): bool
    {
        if (! str_starts_with($path, '/admin/')) {
            return false;
        }

        return preg_match('#^/admin/(inventory|policies|policy-versions|backup-sets)(/|$)#', $path) === 1;
    }
}
