<?php

declare(strict_types=1);

namespace apexl\Io\modules\jobs\Tests;

use apexl\Config\ConfigurationInterface;
use apexl\Io\modules\jobs\Command\RunJobsCommand;
use apexl\Io\modules\jobs\Interface\QueuedTaskFinderInterface;
use apexl\Io\modules\jobs\Service\TaskRunnerService;
use apexl\Io\TestInfrastructure\FeatureTest;
use Generator;
use PHPUnit\Framework\Attributes\Test;
use Symfony\Component\Console\Tester\CommandTester;

use function apexl\Io\container;

/**
 * `jobs:run --limit=100` used to raise a TypeError before running a single job.
 *
 * Symfony returns an option's declared default with the type it was declared with, but
 * anything typed on the command line as a string — so the untouched default (int 10)
 * satisfied TaskRunnerService::run(?int $limit) and every explicit value did not. The
 * option therefore appeared to be unusable, and every worker was pinned to ten jobs a pass
 * whatever its cron entry or unit file said.
 */
final class RunJobsCommandTest extends FeatureTest
{
    /**
     * Records what the runner asked for. A fake rather than a mock because
     * TaskRunnerService is final, and the limit is only observable where it lands.
     */
    private function recordingFinder(): QueuedTaskFinderInterface
    {
        return new class implements QueuedTaskFinderInterface {
            public ?int $limit = null;

            public function tasks(int $limit = 10): Generator
            {
                $this->limit = $limit;

                yield from [];
            }
        };
    }

    private function runWith(array $input): ?int
    {
        $finder = $this->recordingFinder();

        $command = new RunJobsCommand(
            new TaskRunnerService($finder, container()->get(ConfigurationInterface::class))
        );

        new CommandTester($command)->execute($input);

        return $finder->limit;
    }

    #[Test]
    public function anExplicitLimitIsHonoured(): void
    {
        self::assertSame(3, $this->runWith(['--limit' => '3']));
    }

    #[Test]
    public function theDefaultIsUnchanged(): void
    {
        self::assertSame(10, $this->runWith([]));
    }

    /**
     * A bare `--limit` gives null, which is not "unlimited" anywhere downstream — the
     * finder's parameter is not nullable, and null would make its loop condition false and
     * run nothing. It means the default.
     */
    #[Test]
    public function aValuelessLimitFallsBackToTheDefault(): void
    {
        self::assertSame(10, $this->runWith(['--limit' => null]));
    }
}
