<?php

declare(strict_types=1);

namespace App\Services\SupportCenter;

/**
 * 生成建议在服务器终端 / SSH / 面板终端执行的系统更新命令。
 */
final class HostSystemUpdateCliCommandBuilder
{
    /**
     * @return array{cd_command: string, run_command: string, combined: string, project_path: string, uses_detected_php: bool, cli_php_substituted_from_cgi: bool}
     */
    public function buildTerminalScript(string $executionNo): array
    {
        $executionNo = trim($executionNo);
        $projectPath = base_path();
        $cdCommand = $this->buildChangeDirectoryCommand();
        $runCommand = $this->buildRunCommand($executionNo);
        $phpBinary = $this->resolveSuggestedPhpBinary();
        $rawBinary = $this->resolveRawPhpBinary();

        return [
            'cd_command' => $cdCommand,
            'run_command' => $runCommand,
            'combined' => $cdCommand.PHP_EOL.$runCommand,
            'project_path' => $projectPath,
            'uses_detected_php' => $phpBinary !== null,
            'cli_php_substituted_from_cgi' => $rawBinary !== null
                && $phpBinary !== null
                && $this->isPhpCgiBinaryPath($rawBinary)
                && $phpBinary !== $rawBinary,
        ];
    }

    public function buildChangeDirectoryCommand(): string
    {
        return 'cd '.$this->shellQuote(base_path());
    }

    public function buildRunCommand(?string $executionNo = null): string
    {
        $executionNo = trim((string) $executionNo);

        if ($executionNo === '') {
            return $this->artisanInvocation().' dberp:system-update:run';
        }

        return $this->artisanInvocation().' dberp:system-update:run '.$this->shellQuote($executionNo);
    }

    /**
     * @return array{cd_command: string, run_command: string, combined: string, project_path: string, uses_detected_php: bool, cli_php_substituted_from_cgi: bool}
     */
    public function buildFullTerminalScript(): array
    {
        return $this->buildTerminalScript('');
    }

    public function resolveSuggestedPhpBinary(): ?string
    {
        $binary = $this->resolveRawPhpBinary();

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

        return $this->normalizePhpBinaryForCli($binary);
    }

    private function resolveRawPhpBinary(): ?string
    {
        if (! defined('PHP_BINARY')) {
            return null;
        }

        $binary = trim((string) PHP_BINARY);

        if ($binary === '' || ! is_file($binary) || ! is_executable($binary)) {
            return null;
        }

        return $binary;
    }

    private function normalizePhpBinaryForCli(string $binary): string
    {
        if (! $this->isPhpCgiBinaryPath($binary)) {
            return $binary;
        }

        $cliBinary = (string) preg_replace('/php-cgi(\.exe)?$/iu', 'php$1', $binary);

        if ($cliBinary !== $binary && is_file($cliBinary) && is_executable($cliBinary)) {
            return $cliBinary;
        }

        return $binary;
    }

    private function isPhpCgiBinaryPath(string $binary): bool
    {
        return (bool) preg_match('/php-cgi(?:\.exe)?$/iu', $binary);
    }

    private function artisanInvocation(): string
    {
        $phpBinary = $this->resolveSuggestedPhpBinary();

        if ($phpBinary !== null) {
            if (PHP_OS_FAMILY === 'Windows') {
                return '& '.$this->shellQuote($phpBinary).' artisan';
            }

            return $this->shellQuote($phpBinary).' artisan';
        }

        return 'php artisan';
    }

    private function shellQuote(string $value): string
    {
        if (PHP_OS_FAMILY === 'Windows') {
            return '"'.str_replace('"', '\"', $value).'"';
        }

        return escapeshellarg($value);
    }
}
