<?php

declare(strict_types=1);

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

namespace App\Services;

use App\Filament\Pages\HostSystemUpdateCenterPage;
use App\Filament\Pages\SiteOnboardingCenterPage;
use App\Models\DberpSystemUpdateNotice;
use App\Models\User;
use App\Services\SupportCenter\HostSystemUpdateEntitlementGate;
use App\Support\DberpProductVersion;
use App\Support\HostSystemAuthorizationCenterDisplay;
use App\Support\HostSystemUpdateCenterDisplay;
use Illuminate\Support\Facades\Schema;

/**
 * 系统更新本地提醒的只读查询与用户级已读/忽略（不访问远端 API）。
 */
final class DberpSystemUpdateNoticeService
{
    public const ENTITLEMENT_STATE_ACTIONABLE = 'actionable';

    public const ENTITLEMENT_STATE_ONBOARDING_REQUIRED = 'onboarding_required';

    public const ENTITLEMENT_STATE_CONNECTIVITY_REQUIRED = 'connectivity_required';

    public function __construct(
        private readonly DberpPermissionService $permissionService,
        private readonly DberpProductVersion $productVersion,
        private readonly HostSystemUpdateCenterDisplay $updateCenterDisplay,
        private readonly HostSystemUpdateEntitlementGate $entitlementGate,
        private readonly HostSystemAuthorizationCenterDisplay $authorizationCenterDisplay,
    ) {}

    public function canUserViewNotices(?User $user): bool
    {
        return $this->permissionService->allows($user, DberpPermissionService::VIEW_HOST_SYSTEM_UPDATE);
    }

    public function noticesTableExists(): bool
    {
        return Schema::hasTable('dberp_system_update_notices');
    }

    public function latestActiveNoticeFor(?User $user): ?DberpSystemUpdateNotice
    {
        if (! $this->noticesTableExists() || $user === null || ! $this->canUserViewNotices($user)) {
            return null;
        }

        return DberpSystemUpdateNotice::query()
            ->forUser((int) $user->getKey())
            ->active()
            ->orderByDesc('created_at')
            ->limit(1)
            ->first();
    }

    public function markAsRead(User $user, int $noticeId): bool
    {
        if (! $this->noticesTableExists() || ! $this->canUserViewNotices($user)) {
            return false;
        }

        $notice = $this->findOwnedActiveNotice($user, $noticeId);

        if ($notice === null || $notice->read_at !== null) {
            return $notice !== null;
        }

        $notice->read_at = now()->utc();
        $notice->save();

        return true;
    }

    public function dismiss(User $user, int $noticeId): bool
    {
        if (! $this->noticesTableExists() || ! $this->canUserViewNotices($user)) {
            return false;
        }

        $notice = $this->findOwnedActiveNotice($user, $noticeId);

        if ($notice === null || $notice->notice_level === DberpSystemUpdateNotice::NOTICE_LEVEL_REQUIRED) {
            return false;
        }

        if ($notice->dismissed_at !== null) {
            return $notice !== null;
        }

        $notice->dismissed_at = now()->utc();
        $notice->save();

        return true;
    }

    /**
     * @return array{
     *     badge: string,
     *     color: string,
     *     tooltip: string,
     * }|null
     */
    public function navigationBadgeDataFor(?User $user): ?array
    {
        $notice = $this->latestActiveNoticeFor($user);

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

        $tone = $this->resolvePresentation($user, $notice)['banner_tone'];

        return [
            'badge' => (string) __('dberp.system_update_notice.navigation.badge'),
            'color' => $tone === 'primary' ? 'primary' : 'warning',
            'tooltip' => $this->bannerTitleFor($notice),
        ];
    }

    /**
     * @return array{
     *     notice_id: int,
     *     title: string,
     *     banner_description: string,
     *     banner_tone: string,
     *     entitlement_state: string,
     *     summary: ?string,
     *     latest_version: ?string,
     *     last_checked_at_label: ?string,
     *     update_center_url: string,
     *     site_onboarding_url: ?string,
     *     primary_action_label: string,
     *     primary_action_url: string,
     *     secondary_action_label: ?string,
     *     secondary_action_url: ?string,
     *     local_snapshot_hint: string,
     * }|null
     */
    public function panelViewDataFor(?User $user): ?array
    {
        $notice = $this->latestActiveNoticeFor($user);

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

        $presentation = $this->resolvePresentation($user, $notice);
        $updateCenterUrl = HostSystemUpdateCenterPage::getUrl(panel: 'admin');
        $siteOnboardingUrl = $this->siteOnboardingUrlFor($user);
        $actions = $this->resolveBannerActions($presentation, $updateCenterUrl, $siteOnboardingUrl);

        return [
            'notice_id' => (int) $notice->getKey(),
            'title' => $this->bannerTitleFor($notice),
            'banner_description' => $presentation['banner_description'],
            'banner_tone' => $presentation['banner_tone'],
            'entitlement_state' => $presentation['entitlement_state'],
            'summary' => $this->nullableTrimmedString($notice->summary),
            'latest_version' => $this->nullableTrimmedString($notice->latest_version),
            'last_checked_at_label' => $this->updateCenterDisplay->lastCheckedAtLabel(),
            'update_center_url' => $updateCenterUrl,
            'site_onboarding_url' => $siteOnboardingUrl,
            'primary_action_label' => $actions['primary_action_label'],
            'primary_action_url' => $actions['primary_action_url'],
            'secondary_action_label' => $actions['secondary_action_label'],
            'secondary_action_url' => $actions['secondary_action_url'],
            'local_snapshot_hint' => (string) __('dberp.system_update_notice.hints.local_snapshot'),
            'dismissible' => $notice->notice_level !== DberpSystemUpdateNotice::NOTICE_LEVEL_REQUIRED,
        ];
    }

    public function bannerTitleFor(DberpSystemUpdateNotice $notice): string
    {
        $version = $this->nullableTrimmedString($notice->latest_version)
            ?? (string) __('dberp.system_update_center.values.unknown_latest');

        return (string) __('dberp.system_update_notice.banner.title', [
            'product' => $this->productVersion->publicProductName(),
            'version' => $version,
        ]);
    }

    /**
     * @return array{
     *     entitlement_state: string,
     *     banner_tone: string,
     *     banner_description: string,
     * }
     */
    private function resolvePresentation(?User $user, ?DberpSystemUpdateNotice $notice = null): array
    {
        if ($this->entitlementGate->canCheckUpdate()) {
            if ($notice?->notice_level === DberpSystemUpdateNotice::NOTICE_LEVEL_REQUIRED) {
                return [
                    'entitlement_state' => self::ENTITLEMENT_STATE_ACTIONABLE,
                    'banner_tone' => 'warning',
                    'banner_description' => (string) __('dberp.system_update_notice.banner.description_required'),
                ];
            }

            return [
                'entitlement_state' => self::ENTITLEMENT_STATE_ACTIONABLE,
                'banner_tone' => 'primary',
                'banner_description' => (string) __('dberp.system_update_notice.banner.description'),
            ];
        }

        if (! $this->authorizationCenterDisplay->hasSiteBinding()) {
            return [
                'entitlement_state' => self::ENTITLEMENT_STATE_ONBOARDING_REQUIRED,
                'banner_tone' => 'warning',
                'banner_description' => (string) __('dberp.system_update_notice.banner.description_onboarding_required'),
            ];
        }

        return [
            'entitlement_state' => self::ENTITLEMENT_STATE_CONNECTIVITY_REQUIRED,
            'banner_tone' => 'warning',
            'banner_description' => (string) __('dberp.system_update_notice.banner.description_connectivity_required'),
        ];
    }

    /**
     * @param  array{
     *     entitlement_state: string,
     *     banner_tone: string,
     *     banner_description: string,
     * }  $presentation
     * @return array{
     *     primary_action_label: string,
     *     primary_action_url: string,
     *     secondary_action_label: ?string,
     *     secondary_action_url: ?string,
     * }
     */
    private function resolveBannerActions(array $presentation, string $updateCenterUrl, ?string $siteOnboardingUrl): array
    {
        if ($presentation['entitlement_state'] === self::ENTITLEMENT_STATE_ONBOARDING_REQUIRED
            && $siteOnboardingUrl !== null) {
            return [
                'primary_action_label' => (string) __('dberp.system_update_notice.actions.open_site_onboarding'),
                'primary_action_url' => $siteOnboardingUrl,
                'secondary_action_label' => (string) __('dberp.system_update_notice.actions.view_release_notes'),
                'secondary_action_url' => $updateCenterUrl,
            ];
        }

        return [
            'primary_action_label' => (string) __('dberp.system_update_notice.actions.view_update'),
            'primary_action_url' => $updateCenterUrl,
            'secondary_action_label' => null,
            'secondary_action_url' => null,
        ];
    }

    private function siteOnboardingUrlFor(?User $user): ?string
    {
        if ($user === null || ! $this->permissionService->allows($user, DberpPermissionService::VIEW_SITE_ONBOARDING)) {
            return null;
        }

        return SiteOnboardingCenterPage::getUrl(panel: 'admin');
    }

    private function findOwnedActiveNotice(User $user, int $noticeId): ?DberpSystemUpdateNotice
    {
        return DberpSystemUpdateNotice::query()
            ->forUser((int) $user->getKey())
            ->active()
            ->whereKey($noticeId)
            ->first();
    }

    private function nullableTrimmedString(?string $value): ?string
    {
        if ($value === null) {
            return null;
        }

        $trimmed = trim($value);

        return $trimmed === '' ? null : $trimmed;
    }
}
