<?php

declare(strict_types=1);

namespace App\Services\SupportCenter;

use App\Exceptions\SupportCenter\HostSystemUpdatePostApplyException;
use App\Models\HostSystemUpdateExecution;
use RuntimeException;

/** 在全新的 PHP CLI 进程中执行一个迁移批次。 */
final class HostSystemUpdateMigrationBatchProcessRunner implements HostSystemUpdateMigrationBatchExecutor
{
    public const RESULT_PREFIX = 'DBERP_UPDATE_MIGRATION_RESULT=';

    public function __construct(
        private readonly ProcessHostSystemUpdateArtisanCommandHandler $processHandler,
    ) {}

    public function run(HostSystemUpdateExecution $execution): HostSystemUpdateMigrationBatchResult
    {
        $result = $this->processHandler->execute(
            'dberp:update-migrate-batch',
            ['--execution='.(string) $execution->execution_no],
            max(30, (int) config('dberp.system_update.artisan_timeout_seconds', 300)),
        );

        if (! $result->succeeded()) {
            throw HostSystemUpdatePostApplyException::migrationFailed(
                new RuntimeException($result->stderr !== '' ? $result->stderr : $result->stdout),
            );
        }

        $payload = $this->extractPayload($result->stdout);

        return new HostSystemUpdateMigrationBatchResult(
            stageCompleted: (bool) ($payload['stage_completed'] ?? false),
            progress: HostSystemUpdateMigrationProgress::fromArray(
                is_array($payload['progress'] ?? null) ? $payload['progress'] : [],
            ),
            executedMigrationNames: array_values(array_filter(
                is_array($payload['executed'] ?? null) ? $payload['executed'] : [],
                static fn (mixed $name): bool => is_string($name),
            )),
        );
    }

    /** @return array<string, mixed> */
    private function extractPayload(string $stdout): array
    {
        foreach (array_reverse(preg_split('/\R/', $stdout) ?: []) as $line) {
            if (! str_starts_with($line, self::RESULT_PREFIX)) {
                continue;
            }

            $payload = json_decode(substr($line, strlen(self::RESULT_PREFIX)), true);

            if (is_array($payload)) {
                return $payload;
            }
        }

        throw HostSystemUpdatePostApplyException::migrationFailed(
            new RuntimeException('migration_process_result_missing'),
        );
    }
}
