<?php

declare(strict_types=1);

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

namespace App\Support;

use App\Enums\SupportCenter\HostSystemUpdateExecutionStatus;
use App\Enums\SupportCenter\HostSystemUpdateExecutionStep;
use App\Enums\SupportCenter\HostSystemUpdateRecommendedMethod;
use App\Models\HostSystemAuthorization;
use App\Models\HostSystemUpdateCheck;
use App\Models\HostSystemUpdateExecution;
use App\Models\HostSystemUpdateExecutionLog;
use App\Models\HostSystemUpdatePackage;
use App\Services\SupportCenter\HostSystemAuthorizationEntitlementPresenter;
use App\Services\SupportCenter\HostSystemAuthorizationStatusPresenter;
use App\Services\SupportCenter\HostSystemUpdateCliCommandBuilder;
use App\Services\SupportCenter\HostSystemUpdateEntitlementGate;
use App\Services\SupportCenter\HostSystemUpdateEnvironmentDiagnosticReport;
use App\Services\SupportCenter\HostSystemUpdateEnvironmentDiagnosticService;
use App\Services\SupportCenter\HostSystemUpdatePackageStatusPresenter;
use App\Services\SupportCenter\HostSystemUpdateRecoveryGuidanceService;
use App\Services\SupportCenter\HostSystemUpdateStatusPresenter;
use App\Services\SupportCenter\HostSystemUpdateStepPresenter;
use Illuminate\Support\Carbon;

/**
 * 系统更新中心页只读展示数据（面向业务文案，不暴露密钥与原始技术细节）。
 */
final class HostSystemUpdateCenterDisplay
{
    private const EXECUTION_LOG_LIMIT = 15;

    public function __construct(
        private readonly DberpProductVersion $productVersion,
        private readonly HostSystemUpdateStatusPresenter $statusPresenter,
        private readonly HostSystemUpdateStepPresenter $stepPresenter,
        private readonly HostSystemUpdatePackageStatusPresenter $packageStatusPresenter,
        private readonly HostSystemAuthorizationStatusPresenter $authorizationStatusPresenter,
        private readonly HostSystemAuthorizationEntitlementPresenter $entitlementPresenter,
        private readonly HostSystemUpdateEntitlementGate $entitlementGate,
        private readonly HostSystemAuthorizationCenterDisplay $authorizationDisplay,
        private readonly HostSystemUpdateEnvironmentDiagnosticService $environmentDiagnosticService,
        private readonly HostSystemUpdateCliCommandBuilder $cliCommandBuilder,
        private readonly HostSystemUpdateRecoveryGuidanceService $recoveryGuidanceService,
    ) {}

    public function recommendedUpdateMethod(): HostSystemUpdateRecommendedMethod
    {
        $report = $this->environmentDiagnosticReport();

        if (! $report->canRunBrowserUpdate) {
            return HostSystemUpdateRecommendedMethod::BrowserBlockedUseTerminal;
        }

        if ($report->warningCount > 0) {
            return HostSystemUpdateRecommendedMethod::BrowserRisky;
        }

        return HostSystemUpdateRecommendedMethod::BrowserAvailable;
    }

    /**
     * @return array<string, mixed>|null
     */
    public function recommendedUpdateMethodPanelData(): ?array
    {
        if (! $this->entitlementGate->canExecuteUpdate()) {
            return null;
        }

        $method = $this->recommendedUpdateMethod();

        return [
            'label' => (string) __('dberp.system_update_center.recommended_method.panel_label'),
            'method_label' => $method->label(),
            'badge_color' => $method->badgeColor(),
            'summary' => (string) __('dberp.system_update_center.recommended_method.summaries.'.$method->value),
            'detail' => (string) __('dberp.system_update_center.recommended_method.details.'.$method->value),
        ];
    }

    public function shouldSuggestCliUpdate(): bool
    {
        if (! $this->entitlementGate->canExecuteUpdate()) {
            return false;
        }

        return $this->recommendedUpdateMethod() !== HostSystemUpdateRecommendedMethod::BrowserAvailable;
    }

    public function shouldShowTerminalUpdatePanel(): bool
    {
        return $this->entitlementGate->canExecuteUpdate();
    }

    /**
     * @return array<string, mixed>|null
     */
    public function terminalUpdatePanelData(): ?array
    {
        if (! $this->shouldShowTerminalUpdatePanel()) {
            return null;
        }

        $method = $this->recommendedUpdateMethod();
        $execution = $this->cliSuggestedExecution();
        $resumesExistingTask = $this->shouldOfferCopyableCliCommands($execution, $method);
        $script = $this->resolveTerminalCommandScript($execution, $method);
        $commandsReady = $script !== null;

        $introKey = match ($method) {
            HostSystemUpdateRecommendedMethod::BrowserBlockedUseTerminal => 'intro_blocked',
            HostSystemUpdateRecommendedMethod::BrowserRisky => 'intro_risky',
            HostSystemUpdateRecommendedMethod::BrowserAvailable => 'intro_optional',
        };

        /** @var list<string> $guidanceLines */
        $guidanceLines = (array) trans('dberp.system_update_center.terminal.guidance_lines');

        $usesCompactFallbackLayout = in_array($method, [
            HostSystemUpdateRecommendedMethod::BrowserAvailable,
            HostSystemUpdateRecommendedMethod::BrowserRisky,
        ], true);

        return array_merge(
            [
                'variant' => $method->prefersTerminalUpdate() ? 'prominent' : 'optional',
                'show_intro' => ! $usesCompactFallbackLayout,
                'show_guidance' => ! $usesCompactFallbackLayout,
                'intro' => (string) __('dberp.system_update_center.terminal.'.$introKey),
                'guidance_lines' => array_values(array_filter($guidanceLines, static fn (mixed $line): bool => is_string($line) && $line !== '')),
                ...$this->terminalCommandCopyLabels(),
                'has_execution' => $execution instanceof HostSystemUpdateExecution,
                'execution_hint' => $this->resolveTerminalExecutionHint(
                    $execution,
                    $method,
                    $commandsReady,
                    $resumesExistingTask,
                ),
                'optional_heading' => in_array($method, [
                    HostSystemUpdateRecommendedMethod::BrowserAvailable,
                    HostSystemUpdateRecommendedMethod::BrowserRisky,
                ], true)
                    ? (string) __('dberp.system_update_center.terminal.optional_heading_fallback')
                    : (string) __('dberp.system_update_center.terminal.optional_heading'),
            ],
            $this->buildCopyableTerminalCommandFields($script),
        );
    }

    /**
     * 更新进行中时展示的可复制命令（与「更新环境与方式」区逻辑一致）。
     *
     * @return array<string, mixed>|null
     */
    public function updateProgressTerminalPanelData(): ?array
    {
        if (! $this->isUpdateRunning()) {
            return null;
        }

        $execution = $this->cliSuggestedExecution();
        $method = $this->recommendedUpdateMethod();

        $script = $this->resolveTerminalCommandScript($execution, $method);

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

        return array_merge(
            [
                'heading' => (string) __('dberp.system_update_center.terminal.progress_cli_heading'),
                'execution_hint' => (string) __('dberp.system_update_center.cli.panel_execution_hint'),
                'show_intro' => false,
                'show_guidance' => false,
            ],
            $this->terminalCommandCopyLabels(),
            $this->buildCopyableTerminalCommandFields($script),
        );
    }

    /**
     * @return array{cd_command: string, run_command: string, combined: string, project_path: string, uses_detected_php: bool}|null
     */
    private function resolveTerminalCommandScript(
        ?HostSystemUpdateExecution $execution,
        HostSystemUpdateRecommendedMethod $method,
    ): ?array {
        if ($this->shouldOfferCopyableCliCommands($execution, $method)) {
            return $this->cliCommandBuilder->buildTerminalScript((string) $execution->execution_no);
        }

        if ($this->isUpdateRunning()) {
            return null;
        }

        return $this->cliCommandBuilder->buildFullTerminalScript();
    }

    /**
     * @return array{cd_label: string, run_label: string, combined_label: string}
     */
    private function terminalCommandCopyLabels(): array
    {
        return [
            'cd_label' => (string) __('dberp.system_update_center.terminal.cd_label'),
            'run_label' => (string) __('dberp.system_update_center.terminal.run_label'),
            'combined_label' => (string) __('dberp.system_update_center.terminal.combined_label'),
        ];
    }

    /**
     * @param  array{cd_command: string, run_command: string, combined: string, project_path: string, uses_detected_php: bool, cli_php_substituted_from_cgi?: bool}|null  $script
     * @return array<string, mixed>
     */
    /**
     * @param  array<string, mixed>  $script
     */
    private function resolveTerminalPhpHint(array $script): string
    {
        if (($script['cli_php_substituted_from_cgi'] ?? false) === true) {
            return (string) __('dberp.system_update_center.terminal.php_cgi_to_cli_hint');
        }

        if ($script['uses_detected_php'] ?? false) {
            return (string) __('dberp.system_update_center.terminal.php_detected_hint');
        }

        return (string) __('dberp.system_update_center.terminal.php_default_hint');
    }

    private function resolveTerminalCopyHint(): string
    {
        if (PHP_OS_FAMILY === 'Windows') {
            return (string) __('dberp.system_update_center.terminal.copy_hint_windows');
        }

        return (string) __('dberp.system_update_center.terminal.copy_hint');
    }

    private function buildCopyableTerminalCommandFields(?array $script): array
    {
        $commandsReady = $script !== null;

        return [
            'commands_ready' => $commandsReady,
            'cd_command' => $commandsReady ? (string) ($script['cd_command'] ?? '') : null,
            'run_command' => $commandsReady ? (string) ($script['run_command'] ?? '') : null,
            'combined_command' => $commandsReady ? (string) ($script['combined'] ?? '') : null,
            'php_hint' => $commandsReady
                ? $this->resolveTerminalPhpHint($script)
                : null,
            'copy_hint' => $commandsReady
                ? $this->resolveTerminalCopyHint()
                : null,
        ];
    }

    private function executionCannotResumeViaCli(HostSystemUpdateExecution $execution): bool
    {
        return in_array((string) ($execution->status ?? ''), [
            HostSystemUpdateExecutionStatus::Failed->value,
            HostSystemUpdateExecutionStatus::Cancelled->value,
            HostSystemUpdateExecutionStatus::Completed->value,
            HostSystemUpdateExecutionStatus::RolledBack->value,
            HostSystemUpdateExecutionStatus::RollbackFailed->value,
            HostSystemUpdateExecutionStatus::RollbackRequired->value,
            HostSystemUpdateExecutionStatus::RollingBack->value,
        ], true);
    }

    private function shouldOfferCopyableCliCommands(
        ?HostSystemUpdateExecution $execution,
        HostSystemUpdateRecommendedMethod $method,
    ): bool {
        if (! $execution instanceof HostSystemUpdateExecution) {
            return false;
        }

        return ! $this->executionCannotResumeViaCli($execution);
    }

    private function resolveTerminalExecutionHint(
        ?HostSystemUpdateExecution $execution,
        HostSystemUpdateRecommendedMethod $method,
        bool $commandsReady,
        bool $resumesExistingTask,
    ): string {
        if ($commandsReady && $resumesExistingTask) {
            return (string) __('dberp.system_update_center.cli.panel_execution_hint');
        }

        if ($commandsReady) {
            if ($execution instanceof HostSystemUpdateExecution && $this->executionCannotResumeViaCli($execution)) {
                return (string) __('dberp.system_update_center.cli.panel_full_run_after_failure_hint');
            }

            return (string) __('dberp.system_update_center.cli.panel_full_run_hint');
        }

        if ($execution instanceof HostSystemUpdateExecution && $this->executionCannotResumeViaCli($execution)) {
            return (string) __('dberp.system_update_center.cli.panel_terminal_failure_hint');
        }

        return match ($method) {
            HostSystemUpdateRecommendedMethod::BrowserBlockedUseTerminal => (string) __('dberp.system_update_center.cli.panel_blocked_no_execution_hint'),
            HostSystemUpdateRecommendedMethod::BrowserRisky,
            HostSystemUpdateRecommendedMethod::BrowserAvailable => (string) __('dberp.system_update_center.cli.panel_browser_primary_hint'),
        };
    }

    /**
     * @return array<string, mixed>|null
     */
    public function cliRunSuggestionPanelData(): ?array
    {
        return $this->terminalUpdatePanelData();
    }

    public function shouldShowFailureRecoveryGuidance(): bool
    {
        $execution = $this->latestExecution();

        if (! $execution instanceof HostSystemUpdateExecution) {
            return false;
        }

        return in_array((string) ($execution->status ?? ''), [
            HostSystemUpdateExecutionStatus::Failed->value,
            HostSystemUpdateExecutionStatus::RollbackRequired->value,
            HostSystemUpdateExecutionStatus::ManualRecoveryRequired->value,
            HostSystemUpdateExecutionStatus::RollbackFailed->value,
        ], true);
    }

    /**
     * @return array<string, mixed>|null
     */
    public function failureRecoveryPanelData(): ?array
    {
        if (! $this->shouldShowFailureRecoveryGuidance()) {
            return null;
        }

        $execution = $this->latestExecution();
        $guidance = $this->recoveryGuidanceService->resolve($execution);

        if ($guidance->isEmpty()) {
            return null;
        }

        $combinedCommand = null;

        if ($guidance->suggestTerminalUpdate && $execution instanceof HostSystemUpdateExecution) {
            $script = $this->resolveTerminalCommandScript($execution, $this->recommendedUpdateMethod());

            if ($script !== null) {
                $combinedCommand = $script['combined'];
            }
        }

        return [
            'title' => $guidance->title,
            'steps' => $guidance->steps,
            'suggest_terminal' => $guidance->suggestTerminalUpdate,
            'terminal_note' => $guidance->suggestTerminalUpdate
                ? (string) __('dberp.system_update_center.recovery_guidance.terminal_note')
                : null,
            'combined_command' => $combinedCommand,
            'execution_error' => $this->executionErrorMessage(),
        ];
    }

    public function cliSuggestedExecution(): ?HostSystemUpdateExecution
    {
        $active = HostSystemUpdateExecution::query()
            ->whereIn('status', [
                HostSystemUpdateExecutionStatus::Pending->value,
                HostSystemUpdateExecutionStatus::Prechecking->value,
                HostSystemUpdateExecutionStatus::Ready->value,
                HostSystemUpdateExecutionStatus::Running->value,
            ])
            ->latest('id')
            ->first();

        if ($active instanceof HostSystemUpdateExecution) {
            return $active;
        }

        $latest = $this->latestExecution();

        if (! $latest instanceof HostSystemUpdateExecution) {
            return null;
        }

        if (in_array((string) ($latest->status ?? ''), [
            HostSystemUpdateExecutionStatus::Failed->value,
            HostSystemUpdateExecutionStatus::RollbackRequired->value,
            HostSystemUpdateExecutionStatus::RollbackFailed->value,
        ], true)) {
            return $latest;
        }

        return null;
    }

    public function environmentDiagnosticReport(): HostSystemUpdateEnvironmentDiagnosticReport
    {
        return $this->environmentDiagnosticService->diagnose();
    }

    /**
     * @return array<string, mixed>|null
     */
    public function environmentDiagnosticsPanelData(): ?array
    {
        $report = $this->environmentDiagnosticReport();

        return [
            'intro' => (string) __('dberp.system_update_center.environment_diagnostics.panel_intro'),
            'summary_label' => (string) __('dberp.system_update_center.environment_diagnostics.summary_label'),
            'blocking_count_label' => (string) __('dberp.system_update_center.environment_diagnostics.blocking_count_label'),
            'warning_count_label' => (string) __('dberp.system_update_center.environment_diagnostics.warning_count_label'),
            'suggestion_label' => (string) __('dberp.system_update_center.environment_diagnostics.suggestion_label'),
            'summary_message' => $report->summaryMessage,
            'can_run_browser_update' => $report->canRunBrowserUpdate,
            'blocking_count' => $report->blockingCount,
            'warning_count' => $report->warningCount,
            'items' => $report->itemsForPanel(),
        ];
    }

    public function shouldShowUpdateEnvironmentHub(): bool
    {
        return $this->entitlementGate->canExecuteUpdate();
    }

    /**
     * 更新环境与方式（合并环境诊断、推荐方式、命令行指引）。
     *
     * @return array<string, mixed>|null
     */
    public function updateEnvironmentHubPanelData(): ?array
    {
        if (! $this->shouldShowUpdateEnvironmentHub()) {
            return null;
        }

        $method = $this->recommendedUpdateMethod();
        $environmentPanel = $this->environmentDiagnosticsPanelData();
        $terminalPanel = $this->terminalUpdatePanelData();

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

        /** @var list<array<string, mixed>> $allItems */
        $allItems = $environmentPanel['items'];
        $actionableItems = array_values(array_filter(
            $allItems,
            static fn (array $item): bool => in_array((string) ($item['status'] ?? ''), ['fail', 'warn'], true),
        ));
        $passCount = count($allItems) - count($actionableItems);

        $diagnosticsOpenDefault = $method !== HostSystemUpdateRecommendedMethod::BrowserAvailable;

        return [
            'method' => $method->value,
            'method_label' => $method->label(),
            'badge_color' => $method->badgeColor(),
            'headline' => (string) __('dberp.system_update_center.recommended_method.summaries.'.$method->value),
            'blocking_count' => $environmentPanel['blocking_count'],
            'warning_count' => $environmentPanel['warning_count'],
            'blocking_count_label' => $environmentPanel['blocking_count_label'],
            'warning_count_label' => $environmentPanel['warning_count_label'],
            'browser_ok_hint' => $method === HostSystemUpdateRecommendedMethod::BrowserAvailable
                ? (string) __('dberp.system_update_center.hub.browser_ok_hint')
                : null,
            'browser_risky_hint' => $method === HostSystemUpdateRecommendedMethod::BrowserRisky
                ? (string) __('dberp.system_update_center.hub.browser_risky_hint')
                : null,
            'show_terminal_primary' => $method === HostSystemUpdateRecommendedMethod::BrowserBlockedUseTerminal,
            'terminal_details_open' => false,
            'diagnostics_open_default' => $diagnosticsOpenDefault,
            'diagnostics_toggle_label' => (string) __('dberp.system_update_center.hub.diagnostics_toggle'),
            'diagnostics_intro' => (string) __('dberp.system_update_center.hub.diagnostics_intro'),
            'diagnostics_suggestion_label' => $environmentPanel['suggestion_label'],
            'actionable_items' => $actionableItems,
            'pass_count' => $passCount,
            'pass_summary' => $passCount > 0
                ? (string) __('dberp.system_update_center.hub.pass_summary', ['count' => $passCount])
                : null,
            'all_passed_summary' => $passCount > 0 && $actionableItems === []
                ? (string) __('dberp.system_update_center.hub.all_passed', ['count' => $passCount])
                : null,
            'terminal' => $terminalPanel,
            'terminal_learn_more_label' => (string) __('dberp.system_update_center.hub.learn_more_terminal'),
        ];
    }

    public function canRunBrowserUpdate(): bool
    {
        return $this->environmentDiagnosticReport()->canRunBrowserUpdate;
    }

    public function environmentBlockingReason(): ?string
    {
        if ($this->canRunBrowserUpdate()) {
            return null;
        }

        $summary = $this->environmentDiagnosticReport()->firstBlockingSummary();

        if ($summary !== null) {
            return $summary;
        }

        return (string) __('dberp.system_update_center.errors.environment_not_ready');
    }

    public function checkSnapshot(): HostSystemUpdateCheck
    {
        return HostSystemUpdateCheck::singleton();
    }

    public function authorization(): HostSystemAuthorization
    {
        return HostSystemAuthorization::singleton();
    }

    public function latestPackage(): ?HostSystemUpdatePackage
    {
        return HostSystemUpdatePackage::query()
            ->latest('id')
            ->first();
    }

    public function latestExecution(): ?HostSystemUpdateExecution
    {
        return HostSystemUpdateExecution::query()
            ->with(['package'])
            ->latest('id')
            ->first();
    }

    public function pendingExecution(): ?HostSystemUpdateExecution
    {
        return HostSystemUpdateExecution::query()
            ->where('status', HostSystemUpdateExecutionStatus::Pending->value)
            ->latest('id')
            ->first();
    }

    public function productName(): string
    {
        return $this->productVersion->publicProductName();
    }

    public function displayVersion(): string
    {
        return $this->productVersion->publicSemanticVersionLabel();
    }

    public function updateVersion(): int
    {
        return $this->productVersion->updateVersion();
    }

    public function editionLabel(): string
    {
        return $this->productVersion->publicProductEdition();
    }

    public function siteNo(): ?string
    {
        return $this->authorizationDisplay->officialSiteNo();
    }

    public function licenseNo(): ?string
    {
        return $this->authorizationDisplay->licenseNo();
    }

    public function lastCheckedAtLabel(): ?string
    {
        $checkedAt = $this->checkSnapshot()->last_checked_at;

        return $checkedAt instanceof Carbon
            ? $checkedAt->copy()->timezone($this->displayTimezone())->format('Y-m-d H:i')
            : null;
    }

    public function checkResultLabel(): string
    {
        return $this->statusPresenter->checkStatusLabel($this->checkSnapshot()->last_check_status);
    }

    public function checkResultBadgeColor(): string
    {
        return $this->statusPresenter->checkStatusBadgeColor($this->checkSnapshot()->last_check_status);
    }

    public function latestVersionLabel(): ?string
    {
        $version = $this->checkSnapshot()->latest_version;

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

    public function latestUpdateVersionLabel(): ?string
    {
        $version = $this->checkSnapshot()->latest_update_version;

        return $version !== null && (int) $version > 0 ? (string) $version : null;
    }

    public function updateRiskLevelLabel(): string
    {
        $level = strtolower(trim((string) ($this->riskLevelCode() ?? '')));

        return match ($level) {
            'low' => (string) __('dberp.system_update_center.risk.level_low'),
            'medium', 'med', 'moderate' => (string) __('dberp.system_update_center.risk.level_medium'),
            'high', 'critical' => (string) __('dberp.system_update_center.risk.level_high'),
            '' => (string) __('dberp.system_update_center.risk.level_unknown'),
            default => (string) __('dberp.system_update_center.risk.level_unknown'),
        };
    }

    public function updateRiskLevelBadgeColor(): string
    {
        return match (strtolower(trim((string) ($this->riskLevelCode() ?? '')))) {
            'low' => 'success',
            'medium', 'med', 'moderate' => 'warning',
            'high', 'critical' => 'danger',
            default => 'gray',
        };
    }

    public function updateRiskSummary(): ?string
    {
        $summary = $this->riskSummaryText();

        return $summary !== null ? $this->truncateText($summary, 400) : null;
    }

    public function hasUpdateRiskSummary(): bool
    {
        return filled($this->updateRiskSummary());
    }

    public function latestUpdateResultLabel(): string
    {
        $execution = $this->latestExecution();

        if (! $execution instanceof HostSystemUpdateExecution) {
            return (string) __('dberp.system_update_center.values.result_never');
        }

        $status = (string) ($execution->status ?? '');

        return match ($status) {
            HostSystemUpdateExecutionStatus::Completed->value => (string) __('dberp.system_update_center.values.result_success'),
            HostSystemUpdateExecutionStatus::Failed->value => (string) __('dberp.system_update_center.values.result_failed'),
            HostSystemUpdateExecutionStatus::RollbackRequired->value => (string) __('dberp.system_update_center.values.result_needs_recovery'),
            HostSystemUpdateExecutionStatus::ManualRecoveryRequired->value => (string) __('dberp.system_update_center.values.result_needs_recovery'),
            HostSystemUpdateExecutionStatus::RollingBack->value => (string) __('dberp.system_update_center.values.result_recovering'),
            HostSystemUpdateExecutionStatus::RolledBack->value => (string) __('dberp.system_update_center.values.result_recovered'),
            HostSystemUpdateExecutionStatus::RollbackFailed->value => (string) __('dberp.system_update_center.values.result_recovery_failed'),
            HostSystemUpdateExecutionStatus::Running->value,
            HostSystemUpdateExecutionStatus::Prechecking->value,
            HostSystemUpdateExecutionStatus::Ready->value => (string) __('dberp.system_update_center.values.result_in_progress'),
            HostSystemUpdateExecutionStatus::Pending->value => (string) __('dberp.system_update_center.values.result_pending'),
            default => (string) __('dberp.system_update_center.values.result_unknown'),
        };
    }

    public function latestUpdateResultBadgeColor(): string
    {
        $execution = $this->latestExecution();

        if (! $execution instanceof HostSystemUpdateExecution) {
            return 'gray';
        }

        return $this->statusPresenter->badgeColor($execution->status);
    }

    public function latestUpdateResultDetail(): ?string
    {
        $execution = $this->latestExecution();

        if (! $execution instanceof HostSystemUpdateExecution) {
            return null;
        }

        $message = $execution->error_message;

        if (is_string($message) && trim($message) !== '') {
            return $this->truncateText(trim($message), 300);
        }

        $finishedAt = $this->executionFinishedAtLabel();

        if ($finishedAt !== null) {
            return (string) __('dberp.system_update_center.values.result_finished_at', [
                'time' => $finishedAt,
            ]);
        }

        return null;
    }

    public function isUpdateRunning(): bool
    {
        return HostSystemUpdateExecution::query()
            ->whereIn('status', HostSystemUpdateExecutionStatus::activeValues())
            ->exists();
    }

    public function oneClickUnavailableReason(): ?string
    {
        if ($this->isUpdateRunning()) {
            return (string) __('dberp.system_update_center.errors.update_already_running');
        }

        if (! $this->entitlementGate->canCheckUpdate()) {
            return $this->entitlementGate->checkReason();
        }

        if (! $this->entitlementGate->canExecuteUpdate()) {
            return $this->entitlementGate->executeReason();
        }

        if (! $this->canRunBrowserUpdate()) {
            return $this->environmentBlockingReason();
        }

        return null;
    }

    public function mainProgramUpdateAccessSummary(): string
    {
        if (! $this->entitlementGate->canExecuteUpdate()) {
            return $this->entitlementGate->executeReason();
        }

        return (string) __('dberp.system_update_center.guidance.main_program_available');
    }

    public function commercialUpdateAccessSummary(): string
    {
        if ($this->entitlementGate->canExecuteCommercialUpdate()) {
            return (string) __('dberp.system_update_center.guidance.commercial_updates_available');
        }

        return $this->entitlementGate->commercialExecuteReason();
    }

    public function maintenanceNotice(): string
    {
        return (string) __('dberp.system_update_center.workflow.maintenance_notice');
    }

    public function updateAvailableLabel(): string
    {
        if ($this->isForceUpdate() && $this->checkSnapshot()->update_available) {
            return (string) __('dberp.system_update_center.values.update_available_required');
        }

        return $this->checkSnapshot()->update_available
            ? (string) __('dberp.system_update_center.values.update_available_yes')
            : (string) __('dberp.system_update_center.values.update_available_no');
    }

    public function updateAvailableBadgeColor(): string
    {
        if ($this->isForceUpdate() && $this->checkSnapshot()->update_available) {
            return 'danger';
        }

        return $this->checkSnapshot()->update_available ? 'success' : 'gray';
    }

    public function isForceUpdate(): bool
    {
        return (bool) $this->checkSnapshot()->force_update;
    }

    public function hasUpdateAvailable(): bool
    {
        if (! (bool) $this->checkSnapshot()->update_available) {
            return false;
        }

        return ! $this->isLocallyUpToDateWithSnapshot();
    }

    private function isLocallyUpToDateWithSnapshot(): bool
    {
        $snapshot = $this->checkSnapshot();
        $currentDisplay = trim($this->productVersion->displayVersion());
        $latestDisplay = trim((string) ($snapshot->latest_version ?? ''));

        if ($currentDisplay === '' || $latestDisplay === '') {
            return false;
        }

        if ($currentDisplay !== $latestDisplay) {
            return false;
        }

        $latestUpdate = $snapshot->latest_update_version;

        if ($latestUpdate === null) {
            return true;
        }

        return (int) $latestUpdate === $this->productVersion->updateVersion();
    }

    public function releaseNotesSummary(): ?string
    {
        $notes = $this->checkSnapshot()->release_notes;

        if (! is_array($notes) || $notes === []) {
            return null;
        }

        foreach (['zh', 'zh_CN', 'zh-CN', 'cn'] as $localeKey) {
            if (isset($notes[$localeKey]) && is_string($notes[$localeKey]) && trim($notes[$localeKey]) !== '') {
                return $this->truncateText(trim($notes[$localeKey]), 500);
            }
        }

        foreach ($notes as $value) {
            if (is_string($value) && trim($value) !== '') {
                return $this->truncateText(trim($value), 500);
            }
        }

        return null;
    }

    public function lastCheckMessage(): ?string
    {
        if ($this->checkSnapshot()->last_check_status === 'failed') {
            $message = $this->checkSnapshot()->last_check_message;

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

        return null;
    }

    public function authorizationLabel(): string
    {
        return $this->authorizationStatusPresenter->authorizationLabel((bool) $this->authorization()->authorized);
    }

    public function serviceStatusLabel(): string
    {
        return $this->authorizationStatusPresenter->serviceStatusLabel($this->authorization()->service_status);
    }

    public function expiresAtLabel(): ?string
    {
        return $this->authorizationDisplay->expiresAtLabel();
    }

    public function canCheckUpdateLabel(): string
    {
        return $this->entitlementGate->canCheckUpdate()
            ? (string) __('dberp.system_update_center.entitlement.allowed')
            : (string) __('dberp.system_update_center.entitlement.denied');
    }

    public function canExecuteUpdateLabel(): string
    {
        return $this->entitlementGate->canExecuteUpdate()
            ? (string) __('dberp.system_update_center.entitlement.allowed')
            : (string) __('dberp.system_update_center.entitlement.denied');
    }

    public function entitlementReason(): string
    {
        return $this->mainProgramUpdateAccessSummary();
    }

    /**
     * 商业插件 / 增强包权益（与免费主程序在线升级分开展示）。
     *
     * @return list<string>
     */
    public function commercialEntitlementSummaryLines(): array
    {
        $entitlements = $this->authorization()->entitlements;
        $commercialKeys = [
            'can_execute_system_update',
            'can_check_system_update',
            'can_install_plugin',
            'can_update_plugin',
        ];

        if (! is_array($entitlements)) {
            return [];
        }

        $filtered = [];

        foreach ($commercialKeys as $key) {
            if (array_key_exists($key, $entitlements)) {
                $filtered[$key] = $entitlements[$key];
            }
        }

        $rows = $this->entitlementPresenter->primaryEntitlementsForDisplay($filtered === [] ? null : $filtered);

        return array_map(
            static fn (array $row): string => $row['enabled']
                ? (string) __('dberp.system_authorization.entitlement_line.enabled', ['label' => $row['label']])
                : (string) __('dberp.system_authorization.entitlement_line.disabled', ['label' => $row['label']]),
            $rows,
        );
    }

    /**
     * @return list<string>
     */
    public function entitlementSummaryLines(): array
    {
        return $this->commercialEntitlementSummaryLines();
    }

    public function packageIdLabel(): ?string
    {
        $package = $this->latestPackage();
        $packageId = $package?->package_id ?? $this->checkSnapshot()->package_id;

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

    public function packageVersionLabel(): ?string
    {
        $package = $this->latestPackage();
        $version = $package?->version ?? $this->checkSnapshot()->package_version;

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

    public function packageSizeLabel(): ?string
    {
        $package = $this->latestPackage();
        $size = $package?->local_size ?? $package?->package_size ?? $this->checkSnapshot()->package_size;

        return $this->packageStatusPresenter->formatBytes(is_numeric($size) ? (int) $size : null);
    }

    public function checksumStatusLabel(): string
    {
        return $this->packageStatusPresenter->checksumStatusLabel((bool) ($this->latestPackage()?->verified ?? false));
    }

    public function checksumBadgeColor(): string
    {
        return $this->packageStatusPresenter->checksumBadgeColor((bool) ($this->latestPackage()?->verified ?? false));
    }

    public function manifestStatusLabel(): string
    {
        return $this->packageStatusPresenter->manifestStatusLabel($this->latestPackage()?->manifest_status);
    }

    public function manifestBadgeColor(): string
    {
        return $this->packageStatusPresenter->manifestBadgeColor($this->latestPackage()?->manifest_status);
    }

    public function localPackageStatusLabel(): string
    {
        return $this->packageStatusPresenter->downloadStatusLabel($this->latestPackage()?->download_status);
    }

    public function localPackageBadgeColor(): string
    {
        return $this->packageStatusPresenter->downloadStatusBadgeColor($this->latestPackage()?->download_status);
    }

    public function downloadedAtLabel(): ?string
    {
        $downloadedAt = $this->latestPackage()?->downloaded_at;

        return $downloadedAt instanceof Carbon
            ? $downloadedAt->copy()->timezone($this->displayTimezone())->format('Y-m-d H:i')
            : null;
    }

    public function executionNo(): ?string
    {
        $executionNo = $this->latestExecution()?->execution_no;

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

    public function executionStatusLabel(): string
    {
        return $this->statusPresenter->label($this->latestExecution()?->status);
    }

    public function executionStatusBadgeColor(): string
    {
        return $this->statusPresenter->badgeColor($this->latestExecution()?->status);
    }

    public function executionStepLabel(): string
    {
        return $this->stepPresenter->label($this->latestExecution()?->current_step);
    }

    public function executionProgressPercent(): int
    {
        return max(0, min(100, (int) ($this->latestExecution()?->progress_percent ?? 0)));
    }

    public function executionProgressLabel(): string
    {
        return $this->executionProgressPercent().'%';
    }

    public function executionStartedAtLabel(): ?string
    {
        $startedAt = $this->latestExecution()?->started_at;

        return $startedAt instanceof Carbon
            ? $startedAt->copy()->timezone($this->displayTimezone())->format('Y-m-d H:i')
            : null;
    }

    public function executionFinishedAtLabel(): ?string
    {
        $execution = $this->latestExecution();
        $finishedAt = $execution?->finished_at ?? $execution?->failed_at ?? $execution?->cancelled_at;

        return $finishedAt instanceof Carbon
            ? $finishedAt->copy()->timezone($this->displayTimezone())->format('Y-m-d H:i')
            : null;
    }

    public function executionErrorMessage(): ?string
    {
        $message = $this->latestExecution()?->error_message;

        return is_string($message) && trim($message) !== '' ? $this->truncateText(trim($message), 300) : null;
    }

    public function rollbackStatusLabel(): ?string
    {
        $label = $this->statusPresenter->rollbackLabel($this->latestExecution()?->rollback_status);

        return $label;
    }

    public function workflowHint(): string
    {
        return (string) __($this->isForceUpdate()
            ? 'dberp.system_update_center.workflow.required_update_hint'
            : 'dberp.system_update_center.workflow.one_click_hint');
    }

    public function syncExecutionLimitationNote(): ?string
    {
        if (! $this->isUpdateRunning()) {
            return null;
        }

        return (string) __('dberp.system_update_center.workflow.update_in_progress_notice');
    }

    private function riskLevelCode(): ?string
    {
        $requirements = $this->checkSnapshot()->requirements;

        if (is_array($requirements)) {
            $fromRequirements = $requirements['update_risk_level'] ?? $requirements['risk_level'] ?? null;

            if (is_string($fromRequirements) && trim($fromRequirements) !== '') {
                return strtolower(trim($fromRequirements));
            }
        }

        $raw = $this->checkSnapshot()->raw_snapshot;

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

        $package = is_array($raw['package'] ?? null) ? $raw['package'] : [];

        foreach ([$package['update_risk_level'] ?? null, $package['risk_level'] ?? null, $raw['update_risk_level'] ?? null] as $candidate) {
            if (is_string($candidate) && trim($candidate) !== '') {
                return strtolower(trim($candidate));
            }
        }

        return null;
    }

    private function riskSummaryText(): ?string
    {
        $requirements = $this->checkSnapshot()->requirements;

        if (is_array($requirements)) {
            $summary = $requirements['update_risk_summary'] ?? $requirements['risk_summary'] ?? null;

            if (is_string($summary) && trim($summary) !== '') {
                return trim($summary);
            }
        }

        $raw = $this->checkSnapshot()->raw_snapshot;

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

        $package = is_array($raw['package'] ?? null) ? $raw['package'] : [];

        foreach ([$package['update_risk_summary'] ?? null, $package['risk_summary'] ?? null, $raw['update_risk_summary'] ?? null] as $candidate) {
            if (is_string($candidate) && trim($candidate) !== '') {
                return trim($candidate);
            }
        }

        return null;
    }

    /**
     * 更新过程记录（供前台进度面板结构化展示）。
     *
     * @return list<array{time: string, step_label: string, status_label: string, status_color: string, message: string}>
     */
    public function recentExecutionLogEntries(): array
    {
        $execution = $this->latestExecution();

        if (! $execution instanceof HostSystemUpdateExecution) {
            return [];
        }

        return $execution->logs()
            ->latest('id')
            ->limit(self::EXECUTION_LOG_LIMIT)
            ->get()
            ->reverse()
            ->map(fn (HostSystemUpdateExecutionLog $log): array => [
                'time' => $this->formatExecutionLogTime($log),
                'step_label' => $this->stepPresenter->label($log->step),
                'status_label' => $this->stepPresenter->logStatusLabel($log->status),
                'status_color' => $this->stepPresenter->logStatusBadgeColor($log->status),
                'message' => $this->truncateText(trim((string) ($log->message ?? '')), 200),
            ])
            ->values()
            ->all();
    }

    private function formatExecutionLogTime(HostSystemUpdateExecutionLog $log): string
    {
        $timestamp = match ((string) $log->status) {
            HostSystemUpdateExecutionLog::STATUS_STARTED => $log->started_at
                ?? $log->created_at
                ?? $log->finished_at,
            HostSystemUpdateExecutionLog::STATUS_COMPLETED,
            HostSystemUpdateExecutionLog::STATUS_FAILED => $log->finished_at
                ?? $log->created_at
                ?? $log->started_at,
            default => $log->created_at
                ?? $log->started_at
                ?? $log->finished_at,
        };

        return $timestamp instanceof Carbon
            ? $timestamp->copy()->timezone($this->displayTimezone())->format('H:i:s')
            : '--:--:--';
    }

    /**
     * 分批替换期间允许新版展示类与尚未更新的系统档案设置类短暂共存。
     */
    private function displayTimezone(): 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';
    }

    public function isReadyToDismissUpdateProgressPanel(): bool
    {
        $execution = $this->latestExecution();

        if (! $execution instanceof HostSystemUpdateExecution) {
            return true;
        }

        $status = (string) ($execution->status ?? '');

        if (! in_array($status, [
            HostSystemUpdateExecutionStatus::Completed->value,
            HostSystemUpdateExecutionStatus::Failed->value,
        ], true)) {
            return false;
        }

        if ($this->executionHasMaintenanceDownCompletedLog($execution)) {
            return $this->executionHasMaintenanceUpFinishedLog($execution);
        }

        return true;
    }

    public function hasRollbackAvailable(): bool
    {
        $execution = $this->latestExecution();

        if (! $execution instanceof HostSystemUpdateExecution) {
            return false;
        }

        return (string) ($execution->status ?? '') === HostSystemUpdateExecutionStatus::RollbackRequired->value;
    }

    public function canStartUpdate(): bool
    {
        $pending = $this->pendingExecution();

        return $pending instanceof HostSystemUpdateExecution;
    }

    public function manifestParsed(): bool
    {
        return $this->latestPackage()?->manifest_status === HostSystemUpdatePackage::MANIFEST_STATUS_VALID;
    }

    public function packageVerified(): bool
    {
        return (bool) ($this->latestPackage()?->verified ?? false);
    }

    private function executionHasMaintenanceDownCompletedLog(HostSystemUpdateExecution $execution): bool
    {
        return $execution->logs()
            ->where('step', HostSystemUpdateExecutionStep::MaintenanceDown->value)
            ->where('status', HostSystemUpdateExecutionLog::STATUS_COMPLETED)
            ->exists();
    }

    private function executionHasMaintenanceUpFinishedLog(HostSystemUpdateExecution $execution): bool
    {
        return $execution->logs()
            ->where('step', HostSystemUpdateExecutionStep::MaintenanceUp->value)
            ->whereIn('status', [
                HostSystemUpdateExecutionLog::STATUS_COMPLETED,
                HostSystemUpdateExecutionLog::STATUS_FAILED,
            ])
            ->exists();
    }

    private function truncateText(string $text, int $maxLength): string
    {
        if (mb_strlen($text) <= $maxLength) {
            return $text;
        }

        return mb_substr($text, 0, $maxLength - 1).'…';
    }
}
