<?php

declare(strict_types=1);

/**
 * 本文件属于 DBErp 进销存系统。
 *
 * 版权所有 © 2026 北京珑大钜商科技有限公司。保留所有权利。
 *
 * 未经官方授权，不得删除、篡改或隐藏本文件中的版权声明、产品标识与来源信息。
 * 商业授权、官方更新与技术支持服务，请以珑大官方支持中心记录为准。
 *
 * @link https://support.loongdom.cn
 * @see  https://www.dberp.com.cn
 */

namespace App\Providers\Filament;

use App\Exceptions\SystemUpdateMixedStateErrorGuard;
use App\Filament\Auth\DberpAdminLogin;
use App\Filament\Pages\BusinessParametersSettingsPage;
use App\Filament\Pages\DataInitializationImportPage;
use App\Filament\Pages\DberpCommonShortcutsPreferencesPage;
use App\Filament\Pages\DberpComprehensiveSummaryAnalysisPage;
use App\Filament\Pages\DberpCustomerCreditControlPage;
use App\Filament\Pages\DberpInventoryAnalysisPage;
use App\Filament\Pages\DberpPurchaseAnalysisPage;
use App\Filament\Pages\DberpPurchaseInquiryComparisonPage;
use App\Filament\Pages\DberpSalesAnalysisPage;
use App\Filament\Pages\DberpSettlementAgingAnalysisPage;
use App\Filament\Pages\DberpSettlementAnalysisPage;
use App\Filament\Pages\DberpSystemInformationPage;
use App\Filament\Pages\HostPluginCenterPage;
use App\Filament\Pages\HostSystemAuthorizationCenterPage;
use App\Filament\Pages\HostSystemUpdateCenterPage;
use App\Filament\Pages\SiteOnboardingCenterPage;
use App\Filament\Pages\SystemBasicSettingsPage;
use App\Filament\Pages\WorkbenchDashboard;
use App\Filament\Support\DberpAdminSidebarNavigationOrder;
use App\Filament\Support\DberpFilamentActionConfigurator;
use App\Filament\Support\DberpFilamentTableConfigurator;
use App\Http\Controllers\Filament\AdminLocaleSwitchController;
use App\Http\Controllers\Filament\HostSystemUpdateAutoCheckPingController;
use App\Http\Middleware\AssignDberpAuditCorrelation;
use App\Http\Middleware\EnsureDBErpIsInstalled;
use App\Http\Middleware\RegisterDberpPinnedShortcutNavigationItems;
use App\Http\Middleware\RegisterHostPluginNavigationItems;
use App\Http\Middleware\SetAdminLocale;
use App\Models\User;
use App\Services\DberpOfficialNewsFeedService;
use App\Services\DberpPermissionService;
use App\Services\SupportCenter\HostSystemUpdateStepProgressStore;
use App\Support\DberpMaintenanceDownExceptPaths;
use App\Support\DberpProductBranding;
use App\Support\DberpSystemProfileSettings;
use App\Support\HostSystemUpdateCenterDisplay;
use App\Support\PluginIntegration\DberpAiPageContextFilamentBridge;
use Closure;
use Filament\Events\ServingFilament;
use Filament\Forms\Components\DateTimePicker;
use Filament\Http\Middleware\Authenticate;
use Filament\Http\Middleware\AuthenticateSession;
use Filament\Http\Middleware\DisableBladeIconComponents;
use Filament\Http\Middleware\DispatchServingFilamentEvent;
use Filament\Navigation\NavigationGroup;
use Filament\Panel;
use Filament\PanelProvider;
use Filament\Schemas\Schema;
use Filament\Support\Colors\Color;
use Filament\Support\Facades\FilamentTimezone;
use Filament\Support\Facades\FilamentView;
use Filament\Tables\Table;
use Filament\View\PanelsRenderHook;
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
use Illuminate\Cookie\Middleware\EncryptCookies;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken;
use Illuminate\Routing\Middleware\SubstituteBindings;
use Illuminate\Session\Middleware\StartSession;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Route;
use Illuminate\View\Middleware\ShareErrorsFromSession;
use Throwable;

class AdminPanelProvider extends PanelProvider
{
    public function boot(): void
    {
        try {
            $this->configureFilamentDomesticDateTimeDisplay();
            DberpFilamentActionConfigurator::registerDefaults();
            DberpFilamentTableConfigurator::registerDefaults();
            $this->registerAdminLocaleSwitcherRenderHook();
            $this->registerDberpAiGlobalLauncherRenderHooks();
            $this->registerAdminOfficialNewsRenderHooks();
            $this->registerAdminOfficialFeedbackRenderHook();
            $this->registerAdminLocaleSwitcherOnLoginRenderHook();
            $this->registerLoginFilingFooterRenderHook();
            $this->registerAdminSidebarWidthLocaleStyles();
            $this->registerAdminTopbarUtilityStyles();
            $this->registerAdminSpaNavigationCompatRenderHook();
            $this->registerHostSystemUpdateAutoCheckRenderHook();
        } catch (Throwable $throwable) {
            if (class_exists(SystemUpdateMixedStateErrorGuard::class)
                && ! SystemUpdateMixedStateErrorGuard::shouldSuppress($throwable)) {
                throw $throwable;
            }
        }
    }

    public function panel(Panel $panel): Panel
    {
        return $panel
            ->default()
            ->id('admin')
            ->path(trim((string) config('dberp.admin.panel_path', ''), '/'))
            // 关闭 Inter 拉丁子集 preload，避免中文后台首屏触发 Chrome「preloaded but not used」警告。
            ->font(null, null, null, [])
            ->login(DberpAdminLogin::class)
            ->loginRouteSlug('/')
            ->homeUrl(fn (): string => WorkbenchDashboard::getUrl(panel: 'admin'))
            ->viteTheme('resources/css/filament/admin/theme.css')
            ->spa()
            ->spaUrlExceptions([
                '*locale*',
                // 安装向导（routes/install.php）
                '*install*',
                // 文件下载、导出、打印（routes/web.php 下 dberp.* 及同类路径）
                '*download*',
                '*downloads/*',
                '*export*',
                '*exports/*',
                '*print*',
                // 系统更新中心：Web 分步执行与维护模式期间需完整页面生命周期（slug: host-system-update-center）
                '*host-system-update-center*',
                '*system-updates/*',
                '*support/system-updates/*',
                // 珑大支持中心外链
                'https://support.loongdom.cn/*',
            ])
            ->brandName(fn (): string => app(DberpProductBranding::class)->displayName())
            ->brandLogo(view('filament.brand-logo'))
            ->brandLogoHeight('auto')
            ->colors([
                'primary' => Color::hex('#1D4ED8'),
                'info' => Color::hex('#1D4ED8'),
                'success' => Color::hex('#16A34A'),
                'warning' => Color::hex('#F59E0B'),
                'danger' => Color::hex('#DC2626'),
                'gray' => Color::hex('#9CA3AF'),
            ])
            ->globalSearch(true)
            ->globalSearchResourceOptIn()
            ->navigationGroups($this->adminNavigationGroups())
            ->routes(function (): void {
                /** @var array<string, string> $locales */
                $locales = config('dberp.admin.locales', []);

                Route::get('locale/{locale}', AdminLocaleSwitchController::class)
                    ->whereIn('locale', array_keys($locales))
                    ->name('locale.switch');

                Route::post('host-system/update-auto-check/ping', HostSystemUpdateAutoCheckPingController::class)
                    ->name('host-system.update-auto-check.ping');
            })
            ->discoverResources(in: app_path('Filament/Resources'), for: 'App\Filament\Resources')
            ->pages([
                WorkbenchDashboard::class,
                DberpCommonShortcutsPreferencesPage::class,
                DberpCustomerCreditControlPage::class,
                DberpSettlementAgingAnalysisPage::class,
                DberpComprehensiveSummaryAnalysisPage::class,
                DberpSalesAnalysisPage::class,
                DberpPurchaseAnalysisPage::class,
                DberpInventoryAnalysisPage::class,
                DberpSettlementAnalysisPage::class,
                DberpPurchaseInquiryComparisonPage::class,
                DberpSystemInformationPage::class,
                SiteOnboardingCenterPage::class,
                HostSystemAuthorizationCenterPage::class,
                HostSystemUpdateCenterPage::class,
                HostPluginCenterPage::class,
                SystemBasicSettingsPage::class,
                BusinessParametersSettingsPage::class,
                DataInitializationImportPage::class,
            ])
            ->middleware([
                EncryptCookies::class,
                AddQueuedCookiesToResponse::class,
                StartSession::class,
                AssignDberpAuditCorrelation::class,
                SetAdminLocale::class,
                AuthenticateSession::class,
                ShareErrorsFromSession::class,
                EnsureDBErpIsInstalled::class,
                VerifyCsrfToken::class,
                SubstituteBindings::class,
                DisableBladeIconComponents::class,
                DispatchServingFilamentEvent::class,
            ])
            ->authMiddleware([
                Authenticate::class,
                RegisterDberpPinnedShortcutNavigationItems::class,
                RegisterHostPluginNavigationItems::class,
            ]);
    }

    /**
     * @return list<NavigationGroup>
     */
    private function adminNavigationGroups(): array
    {
        return DberpAdminSidebarNavigationOrder::navigationGroups();
    }

    /**
     * 后台 Filament 日期/时间：国内常用数字格式 + 展示时区（见 config app.display_timezone）。
     */
    private function configureFilamentDomesticDateTimeDisplay(): void
    {
        Event::listen(ServingFilament::class, static function (): void {
            try {
                FilamentTimezone::set(self::filamentDisplayTimezone());
            } catch (Throwable $throwable) {
                if (class_exists(SystemUpdateMixedStateErrorGuard::class)
                    && ! SystemUpdateMixedStateErrorGuard::shouldSuppress($throwable)) {
                    throw $throwable;
                }
            }
        });

        Table::configureUsing(static function (Table $table): void {
            $table
                ->defaultDateDisplayFormat('Y-m-d')
                ->defaultDateTimeDisplayFormat('Y-m-d H:i:s')
                ->defaultTimeDisplayFormat('H:i:s');
        });

        Schema::configureUsing(static function (Schema $schema): void {
            $schema
                ->defaultDateDisplayFormat('Y-m-d')
                ->defaultDateTimeDisplayFormat('Y-m-d H:i:s')
                ->defaultTimeDisplayFormat('H:i:s');
        });

        DateTimePicker::configureUsing(static function (DateTimePicker $component): void {
            $component
                ->defaultDateDisplayFormat('Y-m-d')
                ->defaultDateTimeDisplayFormat('Y-m-d H:i')
                ->defaultDateTimeWithSecondsDisplayFormat('Y-m-d H:i:s')
                ->defaultTimeDisplayFormat('H:i')
                ->defaultTimeWithSecondsDisplayFormat('H:i:s');
        });
    }

    /**
     * 在线更新分批替换时，旧宿主可能先落盘本 Provider，此时 DberpSystemProfileSettings 尚无 displayTimezone()。
     */
    private static function filamentDisplayTimezone(): string
    {
        if (method_exists(DberpSystemProfileSettings::class, 'displayTimezone')) {
            return DberpSystemProfileSettings::displayTimezone();
        }

        $configured = (string) config('app.display_timezone', 'UTC');

        return in_array($configured, timezone_identifiers_list(), true) ? $configured : 'UTC';
    }

    /**
     * 语言切换挂在 GLOBAL_SEARCH_AFTER（与顶栏全局搜索同一区域右侧）。
     */
    private function registerAdminLocaleSwitcherRenderHook(): void
    {
        $this->registerMixedStateSafeRenderHook(
            PanelsRenderHook::GLOBAL_SEARCH_AFTER,
            fn (): string => view('filament.hooks.admin-locale-switch')->render(),
        );
    }

    /**
     * 全局 AI 快捷入口挂在 GLOBAL_SEARCH_AFTER，与语言切换同区。
     * 当前页上下文标记挂在 PAGE_START，以便 Filament SPA 换页后仍能被顶栏按钮读取。
     */
    private function registerDberpAiGlobalLauncherRenderHooks(): void
    {
        $this->registerMixedStateSafeRenderHook(
            PanelsRenderHook::GLOBAL_SEARCH_AFTER,
            fn (): string => class_exists(DberpAiPageContextFilamentBridge::class)
                ? DberpAiPageContextFilamentBridge::globalLauncherHtml()
                : '',
        );

        $this->registerMixedStateSafeRenderHook(
            PanelsRenderHook::PAGE_START,
            fn (): string => class_exists(DberpAiPageContextFilamentBridge::class)
                ? DberpAiPageContextFilamentBridge::pageContextMarkerHtml()
                : '',
        );
    }

    /**
     * 顶栏官方资讯入口，以及紧急未读的全站横幅。
     * 不依赖 Support Center 绑定；关闭配置或未登录时不渲染。
     */
    private function registerAdminOfficialNewsRenderHooks(): void
    {
        $this->registerMixedStateSafeRenderHook(
            PanelsRenderHook::USER_MENU_BEFORE,
            function (): string {
                if (! (bool) config('dberp.official_news.enabled', true) || ! Auth::check()) {
                    return '';
                }

                $user = Auth::user();

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

                // 当前没有可展示资讯时不占顶栏；有内容即使已读仍保留入口。
                if (app(DberpOfficialNewsFeedService::class)->feedItems() === []) {
                    return '';
                }

                return view('filament.hooks.admin-official-news-topbar')->render();
            },
        );

        $this->registerMixedStateSafeRenderHook(
            PanelsRenderHook::PAGE_HEADER_WIDGETS_BEFORE,
            function (): string {
                if (! (bool) config('dberp.official_news.enabled', true) || ! Auth::check()) {
                    return '';
                }

                return view('filament.hooks.admin-official-news-critical-banner')->render();
            },
        );
    }

    /**
     * 顶栏用户菜单前：官方问题反馈外链（新标签打开，不采集系统数据）。
     * 挂在用户菜单前，避免与全局搜索、语言切换同排争抢宽度。
     */
    private function registerAdminOfficialFeedbackRenderHook(): void
    {
        $this->registerMixedStateSafeRenderHook(
            PanelsRenderHook::USER_MENU_BEFORE,
            function (): string {
                if (! Auth::check()) {
                    return '';
                }

                return view('filament.hooks.admin-official-feedback-link')->render();
            },
        );
    }

    private function registerAdminLocaleSwitcherOnLoginRenderHook(): void
    {
        $this->registerMixedStateSafeRenderHook(
            PanelsRenderHook::SIMPLE_LAYOUT_START,
            fn (): string => view('filament.hooks.admin-locale-switch-login')->render(),
            scopes: DberpAdminLogin::class,
        );
    }

    private function registerLoginFilingFooterRenderHook(): void
    {
        // 备案信息直接嵌入 simple.blade.php 的 fi-simple-main-ctn 内，无需 hook
    }

    /**
     * 中文界面收窄侧栏为配置宽度；英文等保持 Filament 默认（panel 不调用 sidebarWidth，内联为 20rem）。
     * 必须在 STYLES_AFTER 注入，以覆盖布局内联 :root 变量。
     */
    private function registerAdminSidebarWidthLocaleStyles(): void
    {
        $this->registerMixedStateSafeRenderHook(
            PanelsRenderHook::STYLES_AFTER,
            fn (): string => view('filament.hooks.admin-sidebar-width-locale')->render(),
        );
    }

    private function registerAdminTopbarUtilityStyles(): void
    {
        $this->registerMixedStateSafeRenderHook(
            PanelsRenderHook::STYLES_AFTER,
            fn (): string => view('filament.hooks.admin-topbar-utility-styles')->render(),
        );
    }

    /**
     * SPA 换页后重新初始化侧栏等页面级交互；脚本本身 data-navigate-once，避免重复注册 navigated 监听。
     */
    private function registerAdminSpaNavigationCompatRenderHook(): void
    {
        $this->registerMixedStateSafeRenderHook(
            PanelsRenderHook::SCRIPTS_AFTER,
            fn (): string => view('filament.hooks.admin-spa-nav-compat')->render(),
        );
    }

    /**
     * 后台布局末尾注入轻量 ping：仅对已登录且具备系统更新中心查看权的用户触发 opportunistic 检查。
     */
    private function registerHostSystemUpdateAutoCheckRenderHook(): void
    {
        $this->registerMixedStateSafeRenderHook(
            PanelsRenderHook::LAYOUT_END,
            function (): string {
                if (! (bool) config('dberp.system_update.auto_check.enabled', true)) {
                    return '';
                }

                $user = Auth::user();

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

                if (! app(DberpPermissionService::class)->allows($user, DberpPermissionService::VIEW_HOST_SYSTEM_UPDATE)) {
                    return '';
                }

                if (
                    app(DberpMaintenanceDownExceptPaths::class)->isSystemUpdateCenterRequest(request())
                    || app(HostSystemUpdateCenterDisplay::class)->isUpdateRunning()
                    || app(HostSystemUpdateStepProgressStore::class)->hasNonTerminalSession()
                ) {
                    return '';
                }

                return view('filament.hooks.host-system-update-auto-check-ping', [
                    'pingUrl' => route('filament.admin.host-system.update-auto-check.ping'),
                    'timeoutMs' => max(1000, (int) config('dberp.system_update.auto_check.timeout_seconds', 2) * 1000),
                ])->render();
            },
        );
    }

    /**
     * 在线更新先替换 Provider、后替换 resources 时，旧请求不能因暂缺钩子视图而中断后续批次。
     *
     * @param  Closure(): string  $renderer
     * @param  string|array<string>|null  $scopes
     */
    private function registerMixedStateSafeRenderHook(
        string $hook,
        Closure $renderer,
        string|array|null $scopes = null,
    ): void {
        FilamentView::registerRenderHook(
            $hook,
            static function () use ($renderer): string {
                try {
                    return $renderer();
                } catch (Throwable $throwable) {
                    if (class_exists(SystemUpdateMixedStateErrorGuard::class)
                        && ! SystemUpdateMixedStateErrorGuard::shouldSuppress($throwable)) {
                        throw $throwable;
                    }

                    return '';
                }
            },
            scopes: $scopes,
        );
    }
}
