<?php

declare(strict_types=1);

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::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(config('app.display_timezone', 'UTC'))->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::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
    {
        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
    {
        return $this->checkSnapshot()->update_available ? 'success' : 'gray';
    }

    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(config('app.display_timezone', 'UTC'))->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(config('app.display_timezone', 'UTC'))->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(config('app.display_timezone', 'UTC'))->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) __('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' => $log->started_at instanceof Carbon
                    ? $log->started_at->copy()->timezone(config('app.display_timezone', 'UTC'))->format('H:i:s')
                    : '--:--:--',
                '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();
    }

    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).'…';
    }
}
