<?php

declare(strict_types=1);

namespace App\Services\Supplier;

use App\Exceptions\Supplier\SupplierAuthException;

class PlaywrightBridge
{
    private const TIMEOUT_SECONDS = 75;  // 60s Node timeout + 15s safety buffer

    private const SCRIPT_RELATIVE_PATH = 'playwright/refresh-session.js';

    public function __construct(
        private readonly ProcessFactory $processFactory,
    ) {}

    /**
     * Refresh session via headless Node-subprocess.
     *
     * @return array{phpsessid: string, csrf: string, refreshed_at: string}
     *
     * @throws SupplierAuthException
     */
    public function refreshSession(string $login, string $password, string $url): array
    {
        $process = $this->processFactory->create(
            ['node', self::SCRIPT_RELATIVE_PATH],
            base_path(),
        );
        $process->setInput(json_encode([
            'login' => $login,
            'password' => $password,
            'url' => $url,
        ], JSON_THROW_ON_ERROR));
        $process->setTimeoutSeconds(self::TIMEOUT_SECONDS);
        $process->run();

        if (! $process->isSuccessful()) {
            throw new SupplierAuthException(
                "PlaywrightBridge exit code {$process->getExitCode()}: {$process->getErrorOutput()}",
            );
        }

        $output = json_decode($process->getOutput(), true);
        if (! is_array($output) || ! isset($output['phpsessid'], $output['csrf'])) {
            throw new SupplierAuthException(
                "PlaywrightBridge returned invalid output: {$process->getOutput()}",
            );
        }

        return $output;
    }

    /**
     * Generic Node-скрипт runner: запускает playwright/<script> с JSON stdin,
     * возвращает декодированный JSON stdout. Используется FormProjectChannel
     * (manage-project.js — ярус 2 резерва канала миграции проектов).
     *
     * @param  array<string, mixed>  $args  обязательный ключ 'script'; остальное — payload на stdin.
     * @return array<string, mixed>
     */
    public function run(array $args): array
    {
        $script = $args['script'] ?? null;
        if (! is_string($script) || $script === '') {
            throw new \InvalidArgumentException('PlaywrightBridge::run requires non-empty "script" key');
        }

        $payload = $args;
        unset($payload['script']);

        $process = $this->processFactory->create(
            ['node', 'playwright/'.$script],
            base_path(),
        );
        $process->setInput(json_encode($payload, JSON_THROW_ON_ERROR));
        $process->setTimeoutSeconds(self::TIMEOUT_SECONDS);
        $process->run();

        if (! $process->isSuccessful()) {
            throw new \RuntimeException(
                "PlaywrightBridge::run({$script}) exit code {$process->getExitCode()}: {$process->getErrorOutput()}",
            );
        }

        $output = json_decode($process->getOutput(), true);
        if (! is_array($output)) {
            throw new \RuntimeException(
                "PlaywrightBridge::run({$script}) returned non-array output: {$process->getOutput()}",
            );
        }

        return $output;
    }
}
