<?php

declare(strict_types=1);

namespace NoahVet\Reef\Test\A_Unit\Command\OpenApi;

use NoahVet\Reef\Command\OpenAPI\ClearCommand;
use NoahVet\Reef\Command\OpenAPI\ClearCommandInterface;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\BufferedOutput;

final class ClearCommandTest extends TestCase
{
    private string $tempDir;

    private string $yamlFile;

    private ClearCommandInterface $subject;

    protected function setUp(): void
    {
        parent::setUp();

        $this->tempDir = \sys_get_temp_dir().'/clear_'.\bin2hex(\random_bytes(6));
        $this->yamlFile = \sys_get_temp_dir().'/clear_'.\bin2hex(\random_bytes(6)).'.yaml';

        $this->subject = new ClearCommand();
    }

    protected function tearDown(): void
    {
        $this->removeDir($this->tempDir);
        @\unlink($this->yamlFile);

        parent::tearDown();
    }

    public function testExecuteReturnsFailureWhenPathDoesNotExist(): void
    {
        $input = new ArrayInput([
            'path' => $this->tempDir.'/does_not_exist',
        ]);
        $output = new BufferedOutput();

        $code = $this->subject->run($input, $output);

        self::assertSame(Command::FAILURE, $code);
        self::assertStringContainsString('does not exist', $output->fetch());
    }

    public function testExecuteCleansSingleFileByRemovingEmptyLines(): void
    {
        $content = <<<YAML
            foo: bar

            baz:
              - a

              - b


            YAML;

        \file_put_contents($this->yamlFile, $content);

        $input = new ArrayInput([
            'path' => $this->yamlFile,
        ]);
        $output = new BufferedOutput();

        $code = $this->subject->run($input, $output);

        self::assertSame(Command::SUCCESS, $code);

        $expected = <<<YAML
            foo: bar
            baz:
              - a
              - b

            YAML;

        self::assertSame($expected, (string) \file_get_contents($this->yamlFile));
        self::assertStringContainsString('Cleaned:', $output->fetch());
    }

    public function testExecuteWithDryRunDoesNotModifyFile(): void
    {
        $original = <<<YAML
            foo: bar

            baz:
              - a

              - b


            YAML;

        \file_put_contents($this->yamlFile, $original);

        $input = new ArrayInput([
            'path' => $this->yamlFile,
            '--dry-run' => true,
        ]);
        $output = new BufferedOutput();

        $code = $this->subject->run($input, $output);

        self::assertSame(Command::SUCCESS, $code);

        // File content must remain unchanged in dry-run mode
        self::assertSame($original, (string) \file_get_contents($this->yamlFile));
        self::assertStringContainsString('[DRY-RUN]', $output->fetch());
    }

    public function testExecuteOnDirectoryProcessesOnlyYamlFiles(): void
    {
        @\mkdir($this->tempDir, 0o777, true);

        $yaml1 = $this->tempDir.'/file1.yaml';
        $yaml2 = $this->tempDir.'/file2.yml';
        $txt = $this->tempDir.'/ignore.txt';

        \file_put_contents($yaml1, "a: 1\n\nb: 2\n\n");
        \file_put_contents($yaml2, "c: 3\n\nd: 4\n");
        \file_put_contents($txt, "should\n\nstay\n\nas\n\nis\n");

        $input = new ArrayInput([
            'path' => $this->tempDir,
        ]);
        $output = new BufferedOutput();

        $code = $this->subject->run($input, $output);

        self::assertSame(Command::SUCCESS, $code);

        self::assertSame("a: 1\nb: 2\n", (string) \file_get_contents($yaml1));
        self::assertSame("c: 3\nd: 4\n", (string) \file_get_contents($yaml2));

        // Non-YAML file must not be touched
        self::assertSame("should\n\nstay\n\nas\n\nis\n", (string) \file_get_contents($txt));

        $outputStr = $output->fetch();
        self::assertStringContainsString('Cleaned:', $outputStr);
    }

    private function removeDir(string $dir): void
    {
        if (!\is_dir($dir)) {
            return;
        }

        $it = new \RecursiveDirectoryIterator($dir, \FilesystemIterator::SKIP_DOTS);
        $files = new \RecursiveIteratorIterator($it, \RecursiveIteratorIterator::CHILD_FIRST);

        /** @var \SplFileInfo $f */
        foreach ($files as $f) {
            $path = $f->getPathname();
            $f->isDir() ? @\rmdir($path) : @\unlink($path);
        }

        @\rmdir($dir);
    }
}
