<?php

declare(strict_types=1);

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

use NoahVet\Reef\Command\DataFixtures\SortDataFixtureCommand;
use NoahVet\Reef\Command\DataFixtures\SortDataFixtureCommandInterface;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Tester\CommandTester;
use Symfony\Component\Yaml\Yaml;

class SortDataFixtureCommandTest extends TestCase
{
    private SortDataFixtureCommandInterface $subject;

    private string $tempDir;

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

        $this->subject = new SortDataFixtureCommand();

        $this->tempDir = \sys_get_temp_dir()
            .'/sort_data_fixture_'
            .\uniqid('', true);

        \mkdir($this->tempDir, 0o777, true);
    }

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

        $this->removeDir($this->tempDir);
    }

    public function testExecuteReturnsFailureWhenPathDoesNotExist(): void
    {
        $commandTester = new CommandTester($this->subject);

        $statusCode = $commandTester->execute([
            'path' => $this->tempDir.'/does_not_exist',
        ]);

        self::assertSame(
            Command::FAILURE,
            $statusCode,
        );

        $display = $commandTester->getDisplay();

        self::assertStringContainsString(
            'does not exist',
            $display,
        );
    }

    public function testExecuteSortsYamlFileKeysAndFormatsLists(): void
    {
        $yaml = <<<'YAML'
            customer:
              -
                _ref: pokemon_customer_admin
                birthDate: null
                lastname: null
                firstname: null
                nickname: null
                birthName: null
                gender: null
                companyDescription: null
                id: b0199a11-47aa-4553-975e-4228ebc9fb22
              -
                _ref: pokemon_customer_sacha.ketchum
                lastname: Ketchum
                firstname: Sacha
                nickname: Sacha
                birthName: null
                companyDescription: null
                gender: MALE
                birthDate: '1987-05-22'
                id: 1edb75a1-8764-61de-8f67-7156e29c14e8
            YAML;

        $filePath = $this->createTempFile(
            'fixtures/customer.yaml',
            $yaml,
        );

        $commandTester = new CommandTester($this->subject);

        $statusCode = $commandTester->execute([
            'path' => $filePath,
        ]);

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

        $result = \file_get_contents($filePath);

        self::assertNotFalse($result);

        // 1) On a bien "customer:" avec des items "- id: ..."
        self::assertStringContainsString(
            "customer:\n  - id:",
            $result,
        );

        // 2) Il y a une ligne vide entre les entrées de la liste (même indent)
        self::assertMatchesRegularExpression(
            '/customer:\s*\n\s*- id:[^\n]+(\n[^\n]*)*\n\s*\n\s*- id:/m',
            $result,
        );

        // 3) Vérification du tri des clés dans chaque élément :
        //    - "id" en premier
        //    - "_ref" en dernier
        $parsed = Yaml::parseFile($filePath);

        self::assertIsArray($parsed);
        self::assertArrayHasKey('customer', $parsed);
        self::assertIsArray($parsed['customer']);

        foreach ($parsed['customer'] as $index => $customer) {
            self::assertIsArray($customer);

            $keys = \array_keys($customer);

            self::assertSame(
                'id',
                $keys[0],
                \sprintf('First key of customer[%d] should be "id"', $index),
            );

            $lastKey = $keys[\array_key_last($keys)];

            self::assertSame(
                '_ref',
                $lastKey,
                \sprintf('Last key of customer[%d] should be "_ref"', $index),
            );
        }
    }

    public function testExecuteDoesNotModifyFileInDryRunMode(): void
    {
        // YAML volontairement "désordonné" pour forcer une modification
        $yaml = <<<'YAML'
            foo:
              z: 1
              id: 2
              _ref: 3
            YAML;

        $filePath = $this->createTempFile(
            'fixtures/dry-run.yaml',
            $yaml,
        );

        $originalContent = \file_get_contents($filePath);

        $commandTester = new CommandTester($this->subject);

        $statusCode = $commandTester->execute([
            'path' => $filePath,
            '--dry-run' => true,
        ]);

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

        // En dry-run, le fichier ne doit pas être modifié
        $result = \file_get_contents($filePath);

        self::assertSame(
            $originalContent,
            $result,
            'File should not be modified in dry-run mode',
        );

        // Et comme le contenu aurait été changé, la commande doit annoncer le [DRY-RUN]
        $display = $commandTester->getDisplay();

        self::assertStringContainsString(
            '[DRY-RUN]',
            $display,
        );
    }

    public function testExecuteProcessesDirectoryRecursively(): void
    {
        $yaml1 = <<<'YAML'
            a:
              z: 1
              id: foo
              _meta: bar
            YAML;

        $yaml2 = <<<'YAML'
            b:
              _ref: something
              name: test
              id: bar
            YAML;

        $file1 = $this->createTempFile(
            'dir1/file1.yaml',
            $yaml1,
        );

        $file2 = $this->createTempFile(
            'dir1/subdir/file2.yml',
            $yaml2,
        );

        $commandTester = new CommandTester($this->subject);

        $statusCode = $commandTester->execute([
            'path' => $this->tempDir,
        ]);

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

        $parsed1 = Yaml::parseFile($file1);
        $parsed2 = Yaml::parseFile($file2);

        self::assertSame(
            ['id', 'z', '_meta'],
            \array_keys($parsed1['a']),
        );

        self::assertSame(
            ['id', 'name', '_ref'],
            \array_keys($parsed2['b']),
        );
    }

    private function createTempFile(string $relativePath, string $content): string
    {
        $fullPath = $this->tempDir.'/'.$relativePath;

        $dir = \dirname($fullPath);

        if (!\is_dir($dir)) {
            \mkdir($dir, 0o777, true);
        }

        \file_put_contents($fullPath, $content."\n");

        return $fullPath;
    }

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

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

        /** @var \SplFileInfo $file */
        foreach ($files as $file) {
            if ($file->isDir()) {
                \rmdir($file->getRealPath());

                continue;
            }

            \unlink($file->getRealPath());
        }

        \rmdir($dir);
    }
}
