<?php

declare(strict_types=1);

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

namespace App\Http\Middleware;

use App\Enums\SupportCenter\HostSystemUpdateExecutionStatus;
use App\Models\HostSystemUpdateExecution;
use App\Services\SupportCenter\HostSystemUpdateStepProgressStore;
use App\Support\DberpLivewireRequest;
use App\Support\DberpMaintenanceDownExceptPaths;
use Closure;
use Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance;
use Illuminate\Http\Request;
use Throwable;

/**
 * 维护模式下：存在进行中或待恢复的系统更新任务时，放行 Livewire 分步请求与更新中心页（早退见 down except）。
 */
final class AllowLivewireUpdateDuringSystemUpdateMaintenance extends PreventRequestsDuringMaintenance
{
    public function handle($request, Closure $next)
    {
        if ($this->shouldBypassForActiveSystemUpdate($request)) {
            return $next($request);
        }

        return parent::handle($request, $next);
    }

    private function shouldBypassForActiveSystemUpdate(Request $request): bool
    {
        if (! $this->hasActiveSystemUpdateExecution() && ! $this->hasNonTerminalWebStepSession()) {
            return false;
        }

        return DberpLivewireRequest::isComponentUpdateRequest($request)
            || DberpLivewireRequest::isFrontendAssetRequest($request)
            || app(DberpMaintenanceDownExceptPaths::class)->isSystemUpdateCenterRequest($request);
    }

    private function hasNonTerminalWebStepSession(): bool
    {
        try {
            return app(HostSystemUpdateStepProgressStore::class)->hasNonTerminalSession();
        } catch (Throwable) {
            return false;
        }
    }

    private function hasActiveSystemUpdateExecution(): bool
    {
        try {
            return HostSystemUpdateExecution::query()
                ->whereIn('status', HostSystemUpdateExecutionStatus::maintenanceBypassValues())
                ->exists();
        } catch (Throwable) {
            return false;
        }
    }
}
