<?php
namespace App\Tests\Unit\Service\Cron\UI;

use App\Service\Cron\UI\ConsoleUI;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Output\OutputInterface;

class TestableConsoleUI extends ConsoleUI {
    public function setProgressBar(ProgressBar $progressBar): void
    {
        $this->progressBar = $progressBar;
    }
}

class ConsoleUITest extends TestCase
{
    private mixed $output;

    public function setUp(): void
    {
        \DG\BypassFinals::enable();
        $this->output = $this->getMockBuilder(OutputInterface::class)->disableOriginalConstructor()->getMock();
    }

    /**
     * @param list<string> $methodNames
     * @return \App\Tests\Service\Cron\UI\TestableConsoleUI | MockObject
     */
    private function getConsoleUiMockFor(array $methodNames): \App\Tests\Service\Cron\UI\TestableConsoleUI| MockObject {
        return $this->getMockBuilder(TestableConsoleUI::class)
            ->setConstructorArgs([$this->output])
            ->setMethodsExcept($methodNames)
            ->getMock();
    }

    public function testPrint(): void
    {
        $consoleUI = $this->getConsoleUiMockFor(['print']);

        $this->output->expects(self::once())->method('writeln')->with('foo');

        $consoleUI->print('foo');
    }

    public function testProgress(): void {
        /** @var ProgressBar | MockObject $progressBar */
        $progressBar = $this->getMockBuilder(ProgressBar::class)->disableOriginalConstructor()->getMock();

        $consoleUI = $this->getMockBuilder(TestableConsoleUI::class)
            ->setConstructorArgs([$this->output])
            ->setMethodsExcept(['progress', 'setProgressBar'])
            ->getMock();
        $consoleUI->setProgressBar($progressBar);

        $progressBar->expects(self::once())
            ->method('setProgress')
            ->with(21);
        $progressBar->expects(self::once())
            ->method('setMaxSteps')
            ->with(100);

        $consoleUI->progress(21, 100);
    }

    public function testProgressEnd(): void {
        /** @var ProgressBar | MockObject $progressBar */
        $progressBar = $this->getMockBuilder(ProgressBar::class)->disableOriginalConstructor()->getMock();

        $consoleUI = $this->getConsoleUiMockFor(['progress', 'setProgressBar']);

        $consoleUI->setProgressBar($progressBar);

        $progressBar->expects(self::once())
            ->method('setProgress')
            ->with(100);
        $progressBar->expects(self::once())
            ->method('setMaxSteps')
            ->with(100);
        $consoleUI->expects(self::once())->method('print')->with('');

        $consoleUI->progress(100, 100);
    }
}
