<?php

declare(strict_types=1);

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

namespace App\Services\SupportCenter;

use App\Exceptions\SupportCenter\HostSystemUpdateRecoveryException;
use App\Support\DberpMaintenanceDownCommandOptions;
use Illuminate\Support\Facades\Artisan;
use Throwable;

/**
 * 在当前 PHP 进程内执行 artisan down/up（与 Laravel 维护文件机制一致，不依赖 proc_open）。
 */
final class InProcessHostSystemUpdateMaintenanceCommandHandler implements HostSystemUpdateMaintenanceCommandHandler
{
    public function __construct(
        private readonly DberpMaintenanceDownCommandOptions $maintenanceDownOptions,
    ) {}

    public function executeDown(?int $timeoutSeconds): HostSystemUpdateArtisanCommandResult
    {
        $this->maintenanceDownOptions->applyExceptPathsForDownCommand();

        try {
            return $this->invoke('down', $this->maintenanceDownOptions->artisanArguments());
        } finally {
            $this->maintenanceDownOptions->clearExceptPathsRegistration();
        }
    }

    public function executeUp(?int $timeoutSeconds): HostSystemUpdateArtisanCommandResult
    {
        return $this->invoke('up');
    }

    /**
     * @param  array<string, string|int|bool>  $arguments
     */
    private function invoke(string $command, array $arguments = []): HostSystemUpdateArtisanCommandResult
    {
        $startedAt = now()->utc();
        $startedMicro = microtime(true);

        try {
            $exitCode = Artisan::call($command, $arguments);
            $output = Artisan::output();
        } catch (Throwable) {
            throw HostSystemUpdateRecoveryException::maintenanceCommandFailed($command, 1);
        }

        $finishedAt = now()->utc();
        $durationMs = max(0, (int) round((microtime(true) - $startedMicro) * 1000));

        $result = new HostSystemUpdateArtisanCommandResult(
            command: $command,
            arguments: [],
            exitCode: $exitCode,
            stdout: HostSystemUpdateArtisanOutputSanitizer::sanitize($output),
            stderr: '',
            startedAt: $startedAt,
            finishedAt: $finishedAt,
            durationMs: $durationMs,
        );

        if (! $result->succeeded()) {
            throw HostSystemUpdateRecoveryException::maintenanceCommandFailed($command, $exitCode);
        }

        return $result;
    }
}
