<?php

declare(strict_types=1);

namespace App\Services\SupportCenter;

use App\Enums\SupportCenter\HostSystemUpdateExecutionStep;
use App\Exceptions\SupportCenter\HostSystemUpdatePostApplyException;
use App\Models\HostSystemUpdateExecution;
use Throwable;

/**
 * 在代码/vendor 切换后尽力刷新当前 Web 进程；真正的执行屏障由后续全新 PHP 子进程提供。
 */
final class HostSystemUpdateRuntimeResetService
{
    public function __construct(
        private readonly HostSystemUpdateOpcache $opcache,
        private readonly HostSystemUpdateExecutionLogger $executionLogger,
    ) {}

    public function reset(HostSystemUpdateExecution $execution): void
    {
        $startedAt = now()->utc();
        clearstatcache(true);

        $enabled = false;
        $reset = false;
        $reason = null;

        try {
            $enabled = $this->opcache->shouldReset();
            $reset = ! $enabled || $this->opcache->reset();

            if ($enabled && ! $reset) {
                $reason = $this->failureReason();
            }
        } catch (Throwable $throwable) {
            $enabled = true;
            $reason = 'opcache_reset_exception:'.$throwable::class;
        }

        $diagnostics = $this->diagnosticContext();

        if ($reason !== null
            && array_key_exists('opcache_validate_timestamps', $diagnostics)
            && $diagnostics['opcache_validate_timestamps'] === false) {
            throw HostSystemUpdatePostApplyException::runtimeResetFailed($reason);
        }

        $this->executionLogger->logStepCompleted(
            $execution,
            HostSystemUpdateExecutionStep::ResetRuntime->value,
            (string) __($reason === null
                ? 'dberp.system_update.post_apply.log.runtime_reset_completed'
                : 'dberp.system_update.post_apply.log.runtime_reset_deferred', [
                    'reason' => $reason,
                ]),
            array_merge(
                $diagnostics,
                [
                    'opcache_enabled' => $enabled,
                    'opcache_reset' => $enabled ? $reset : false,
                    'opcache_reset_best_effort' => true,
                    'fresh_process_barrier' => true,
                    'failure_reason' => $reason ?? 'none',
                ],
            ),
            $startedAt,
        );
    }

    private function failureReason(): string
    {
        if (method_exists($this->opcache, 'failureReason')) {
            $reason = $this->opcache->failureReason();

            if (is_string($reason) && $reason !== '') {
                return $reason;
            }
        }

        return 'opcache_reset_failed';
    }

    /** @return array<string, mixed> */
    private function diagnosticContext(): array
    {
        if (! method_exists($this->opcache, 'diagnosticContext')) {
            return [];
        }

        $context = $this->opcache->diagnosticContext();

        return is_array($context) ? $context : [];
    }
}
