<?php

declare(strict_types=1);

namespace Dalten\RuianToUir\Tests\Integration\Converter;

use Dalten\RuianToUir\Converter\Address;
use Dalten\RuianToUir\Converter\Building;
use Dalten\RuianToUir\Converter\CadastralArea;
use Dalten\RuianToUir\Converter\CityPart;
use Dalten\RuianToUir\Converter\ConverterInterface;
use Dalten\RuianToUir\Converter\County;
use Dalten\RuianToUir\Converter\Field\PointCoordinatesConverter;
use Dalten\RuianToUir\Converter\Municipality;
use Dalten\RuianToUir\Converter\MunicipalityPart;
use Dalten\RuianToUir\Converter\Plot;
use Dalten\RuianToUir\Converter\PragueDistrict;
use Dalten\RuianToUir\Converter\Region;
use Dalten\RuianToUir\Converter\Street;
use PHPUnit\Framework\Attributes\CoversNothing;
use PHPUnit\Framework\TestCase;
use proj4php\Proj;
use proj4php\Proj4php;

#[CoversNothing]
final class RealMiniExtractorsTest extends TestCase
{
    public function testExtractorsReadAllAvailableRecordsFromRealMiniXml(): void
    {
        $fixture = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'Fixtures' . DIRECTORY_SEPARATOR . 'xml' . DIRECTORY_SEPARATOR . '20250831_ST_UZSZ-mini.xml';

        $converters = $this->createConverters();

        $expectedByConverter = $this->countExpectedRecordsByConverter($fixture, [
            'County' => 'vf:Vusc',
            'Region' => 'vf:Okres',
            'Municipality' => 'vf:Obec',
            'MunicipalityPart' => 'vf:CastObce',
            'Street' => 'vf:Ulice',
            'Building' => 'vf:StavebniObjekt',
            'Address' => 'vf:AdresniMisto',
            'PragueDistrict' => 'vf:Mop',
            'CityPart' => 'vf:Momc',
            'Plot' => 'vf:Parcela',
            'CadastralArea' => 'vf:KatastralniUzemi',
        ]);

        $actualByConverter = array_fill_keys(array_keys($expectedByConverter), 0);
        $skippedTags = ['vf:Hlavicka', 'vf:Kraje', 'vf:SpravniObvody', 'vf:Zsj'];

        $reader = new \XMLReader();
        $this->assertTrue($reader->open($fixture));

        while ($reader->read()) {
            while (in_array($reader->name, $skippedTags, true)) {
                $reader->next();
            }

            if (\XMLReader::ELEMENT !== $reader->nodeType) {
                continue;
            }

            foreach ($converters as $converter) {
                if ($converter->accepts($reader)) {
                    $converted = $converter->convert($reader);
                    $this->assertSame($converter->getName(), $converted->getItemType());
                    $actualByConverter[$converter->getName()]++;
                }
            }
        }

        $reader->close();

        $this->assertSame($expectedByConverter, $actualByConverter);
    }

    /**
     * @return list<ConverterInterface>
     */
    private function createConverters(): array
    {
        $proj4 = new Proj4php();
        $coord = new PointCoordinatesConverter(
            $proj4,
            new Proj('EPSG:5514', $proj4),
            new Proj('EPSG:4326', $proj4)
        );

        return [
            new County($coord),
            new Region($coord),
            new Municipality($coord),
            new MunicipalityPart($coord),
            new Street($coord),
            new Building($coord),
            new Address($coord),
            new PragueDistrict($coord),
            new CityPart($coord),
            new Plot($coord),
            new CadastralArea($coord),
        ];
    }

    /**
     * @param array<string, string> $tagByConverter
     * @return array<string, int>
     */
    private function countExpectedRecordsByConverter(string $fixture, array $tagByConverter): array
    {
        $dom = new \DOMDocument();
        $this->assertTrue($dom->load($fixture));

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

        $expected = [];
        foreach ($tagByConverter as $converterName => $tag) {
            $nodes = $xpath->query('//'.$tag.'[@gml:id]');
            $this->assertNotFalse($nodes);
            $expected[$converterName] = $nodes->length;
        }

        return $expected;
    }
}
