<?php

declare(strict_types=1);

namespace App\Services;

use App\Filament\Pages\HostSystemUpdateCenterPage;
use App\Models\DberpSystemUpdateNotice;
use App\Models\User;
use App\Support\DberpProductVersion;
use App\Support\HostSystemUpdateCenterDisplay;
use Illuminate\Support\Facades\Schema;

/**
 * 系统更新本地提醒的只读查询与用户级已读/忽略（不访问远端 API）。
 */
final class DberpSystemUpdateNoticeService
{
    public function __construct(
        private readonly DberpPermissionService $permissionService,
        private readonly DberpProductVersion $productVersion,
        private readonly HostSystemUpdateCenterDisplay $updateCenterDisplay,
    ) {}

    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->dismissed_at !== null) {
            return $notice !== null;
        }

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

        return true;
    }

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

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

        return [
            'notice_id' => (int) $notice->getKey(),
            'title' => $this->bannerTitleFor($notice),
            'banner_description' => (string) __('dberp.system_update_notice.banner.description'),
            'summary' => $this->nullableTrimmedString($notice->summary),
            'latest_version' => $this->nullableTrimmedString($notice->latest_version),
            'last_checked_at_label' => $this->updateCenterDisplay->lastCheckedAtLabel(),
            'update_center_url' => HostSystemUpdateCenterPage::getUrl(panel: 'admin'),
            'local_snapshot_hint' => (string) __('dberp.system_update_notice.hints.local_snapshot'),
        ];
    }

    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,
        ]);
    }

    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;
    }
}
