<?php

declare(strict_types=1);

namespace Dalten\RuianToUir\Tests\Integration\Command;

use Dalten\RuianToUir\Command\PareDownXml;
use Dalten\RuianToUir\Converter\ConverterInterface;
use Dalten\RuianToUir\Converter\Result\ResultInterface;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Tester\CommandTester;

#[CoversClass(PareDownXml::class)]
final class PareDownXmlTest extends TestCase
{
    public function testCreatesMiniXmlWithAtMostThreeRecordsPerTypeAndKeepsHeader(): void
    {
        $dir = dirname(__DIR__, 3) . DIRECTORY_SEPARATOR . 'var' . DIRECTORY_SEPARATOR . 'test';
        if (!is_dir($dir)) {
            $this->assertTrue(mkdir($dir, 0777, true) || is_dir($dir));
        }

        $sourceFixture = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'Fixtures' . DIRECTORY_SEPARATOR . 'xml' . DIRECTORY_SEPARATOR . 'pare-down-input.xml';
        $input = $dir . DIRECTORY_SEPARATOR . 'pare-down-input.xml';
        $output = $dir . DIRECTORY_SEPARATOR . 'pare-down-input-mini.xml';

        $this->assertTrue(copy($sourceFixture, $input));

        try {
            $command = new PareDownXml([
                $this->createConverter('County', 'vf:Vusc'),
                $this->createConverter('Region', 'vf:Okres'),
            ]);
            $tester = new CommandTester($command);

            $exitCode = $tester->execute([
                'input-file' => $input,
            ]);

            $this->assertSame(0, $exitCode, $tester->getDisplay());
            $this->assertFileExists($output);

            $dom = new \DOMDocument();
            $this->assertTrue($dom->load($output));

            $xp = new \DOMXPath($dom);
            $xp->registerNamespace('vf', 'urn:cz:isvs:ruian:schemas:VymennyFormatTypy:v1');
            $xp->registerNamespace('gml', 'http://www.opengis.net/gml/3.2');

            $this->assertSame(1, $this->queryLength($xp, '/vf:VymennyFormat/vf:Hlavicka'));
            $this->assertSame(1, $this->queryLength($xp, '/vf:VymennyFormat/vf:Data'));
            $this->assertSame(0, $this->queryLength($xp, '/vf:VymennyFormat/vf:Hlavicka/vf:Vusc'));

            $this->assertSame(3, $this->queryLength($xp, '//vf:Vusc[@gml:id]'));
            $this->assertSame(3, $this->queryLength($xp, '//vf:Okres[@gml:id]'));
        } finally {
            if (file_exists($input)) {
                unlink($input);
            }
            if (file_exists($output)) {
                unlink($output);
            }
        }
    }

    private function queryLength(\DOMXPath $xpath, string $query): int
    {
        $result = $xpath->query($query);
        $this->assertNotFalse($result);

        return $result->length;
    }

    private function createConverter(string $name, string $handledTag): ConverterInterface
    {
        return new readonly class($name, $handledTag) implements ConverterInterface {
            public function __construct(
                private string $name,
                private string $handledTag,
            ) {
            }

            public function accepts(\XMLReader $document): bool
            {
                return \XMLReader::ELEMENT === $document->nodeType && $document->name === $this->handledTag;
            }

            public function convert(\XMLReader $document): ResultInterface
            {
                throw new \LogicException('Not used by pare-down command.');
            }

            public function getName(): string
            {
                return $this->name;
            }
        };
    }
}
