<?php

declare(strict_types=1);

use App\Services\Supplier\Channel\FormProjectChannel;
use App\Services\Supplier\Channel\SupplierProjectChannel;
use App\Services\Supplier\Dto\SupplierProjectDto;
use App\Services\Supplier\PlaywrightBridge;

/*
 * FormProjectChannel (Tier 2) — PHP wrapper над manage-project.js.
 *
 * PlaywrightBridge подменяется stub-ом (extends PlaywrightBridge с пустым
 * конструктором — реальный требует ProcessFactory; run() переопределён целиком,
 * processFactory не трогается).
 */

function bridgeStub(callable $onRun): PlaywrightBridge
{
    return new class($onRun) extends PlaywrightBridge
    {
        /** @var array<string, mixed>|null */
        public ?array $lastArgs = null;

        /** @var callable */
        private $onRun;

        public function __construct(callable $onRun)
        {
            $this->onRun = $onRun;
        }

        public function run(array $args): array
        {
            $this->lastArgs = $args;

            return ($this->onRun)($args);
        }
    };
}

it('FormProjectChannel implements SupplierProjectChannel', function (): void {
    app()->instance(PlaywrightBridge::class, bridgeStub(fn () => []));

    expect(app(FormProjectChannel::class))->toBeInstanceOf(SupplierProjectChannel::class);
});

it('createProject calls PlaywrightBridge with operation=create and returns external_id', function (): void {
    $stub = bridgeStub(fn () => ['external_id' => '12345']);
    app()->instance(PlaywrightBridge::class, $stub);

    $dto = new SupplierProjectDto(
        platform: 'B1', signalType: 'site', uniqueKey: 'foo.com',
        limit: 10, workdays: [1, 2], regions: [], regionsReverse: false, status: 'active',
    );

    $id = app(FormProjectChannel::class)->createProject($dto);

    expect($id)->toBe(12345);
    expect($stub->lastArgs['operation'])->toBe('create');
    expect($stub->lastArgs['script'])->toBe('manage-project.js');
    expect($stub->lastArgs['dto']['name'])->toBe('foo.com');
    expect($stub->lastArgs['dto']['platforms'])->toBe(['B1']);
});

it('updateProject calls bridge with operation=update and externalId', function (): void {
    $stub = bridgeStub(fn () => ['ok' => true]);
    app()->instance(PlaywrightBridge::class, $stub);

    $dto = new SupplierProjectDto(
        platform: 'B1', signalType: 'site', uniqueKey: 'foo.com',
        limit: 20, workdays: [1], regions: [], regionsReverse: false, status: 'active',
    );
    app(FormProjectChannel::class)->updateProject(700123, $dto);

    expect($stub->lastArgs['operation'])->toBe('update');
    expect($stub->lastArgs['externalId'])->toBe(700123);
});

it('listProjects calls bridge with operation=list and returns array', function (): void {
    $stub = bridgeStub(fn () => ['projects' => [['id' => 1, 'name' => 'A']]]);
    app()->instance(PlaywrightBridge::class, $stub);

    $list = app(FormProjectChannel::class)->listProjects();

    expect($list)->toBe([['id' => 1, 'name' => 'A']]);
    expect($stub->lastArgs['operation'])->toBe('list');
});

it('createProject throws when bridge returns empty external_id', function (): void {
    app()->instance(PlaywrightBridge::class, bridgeStub(fn () => ['external_id' => '0']));

    $dto = new SupplierProjectDto(
        platform: 'B1', signalType: 'site', uniqueKey: 'foo.com',
        limit: 10, workdays: [1], regions: [], regionsReverse: false, status: 'active',
    );

    expect(fn () => app(FormProjectChannel::class)->createProject($dto))
        ->toThrow(RuntimeException::class, 'empty external_id');
});
