<?php

namespace Dalten\RuianToUir\Command;

use Dalten\RuianToUir\Command\Helper\FileDownloader;
use Dalten\RuianToUir\Converter\Municipality;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

/**
 * Created by PhpStorm.
 * User: karel
 * Date: 23.02.2017
 * Time: 11:44.
 */
class GetFileList extends Command
{
    public const string DATA_SET_FULL = 'full';
    public const string DATA_SET_DIFF = 'diff';

    /**
     * @param string[] $skippedTags
     */
    public function __construct(
        private readonly FileDownloader $downloader,
        private readonly Municipality $municipalityConverter,
        private readonly array $skippedTags,
        private readonly string $topLevelFullDataPath,
        private readonly string $municipalityFullDataPathPattern,
        private readonly string $changeDataPathPattern)
    {
        parent::__construct();
    }

    protected function configure(): void
    {
        $this->setName('get-file-list');
        $this->setDescription('Získá seznam souborů ke stažení.');
        $this->addArgument(
            'data-set', InputArgument::REQUIRED, 'Celý import a nebo jen změnové soubory? (full, diff.)'
        );
        $this->addOption(
            'from-date', null, InputOption::VALUE_REQUIRED, 'Zahrnout změnové soubory od? (YYYY-MM-DD)',
            date('Y-m-d')
        );
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        /** @var string $type */
        $type = $input->getArgument('data-set');
        $io = new SymfonyStyle($input, $output);
        $verbose = $output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE;

        if (self::DATA_SET_FULL === $type) {
            try {
                $this->printFullDataList($io, $verbose);

                return 0;
            } catch (\Exception $e) {
                $error = $e->getMessage();
            }
        } elseif (self::DATA_SET_DIFF === $type) {
            try {
                $startDate = $input->getOption('from-date');
                if (!\is_string($startDate) || !preg_match('~^\d{4}-\d{2}-\d{2}~', $startDate)) {
                    $io->error('Nesprávný formát data. Použijte formát YYYY-MM-DD.');

                    return 1;
                }

                $this->printDiffDataList($io, $startDate, $verbose);

                return 0;
            } catch (\Exception $e) {
                $error = $e->getMessage();
            }
        } else {
            $error = \sprintf('Neplatný typ importu "%s". [%s, %s]', $type, self::DATA_SET_DIFF, self::DATA_SET_FULL);
        }

        $io->error($error);

        return 1;
    }

    private function printFullDataList(SymfonyStyle $io, bool $verbose = false): void
    {
        $filename = $this->topLevelFullDataPath;
        if ($verbose) {
            $io->comment(\sprintf('Stahuji "%s"', $filename));
        }
        $countyXmlPath = $this->downloader->getFirstLineFromFile($filename);

        $io->writeln($countyXmlPath);

        if ($verbose) {
            $io->comment(\sprintf('Stahuji "%s"', $countyXmlPath));
        }
        $tempPath = $this->downloader->downloadAndGunzipFileToTemp($countyXmlPath);

        if ($verbose) {
            $io->comment(\sprintf('Staženo a rozbaleno do "%s"', $tempPath));
        }

        $municipalityIds = $this->getMunicipalityIds($tempPath, $io, $verbose);
        unlink($tempPath);

        $template = null; // http://vdp.cuzk.cz/vymenny_format/soucasna/20170131_OB_569054_UZSZ.xml.gz
        foreach ($municipalityIds as $municipalityId) {
            $municipalityId = (string) $municipalityId;
            if (!isset($template)) {
                $uri = str_replace('%obec_kod%', $municipalityId, $this->municipalityFullDataPathPattern);
                if ($verbose) {
                    $io->comment(\sprintf('Stahuji "%s"', $uri));
                }
                $path = $this->downloader->getFirstLineFromFile($uri);
                $template = str_replace('_'.$municipalityId.'_', '_%obec_kod%_', $path);
                if ($verbose) {
                    $io->comment(\sprintf('Cesta pro data obce: "%s"', $template));
                }
            } else {
                $path = str_replace('%obec_kod%', $municipalityId, $template);
            }
            $io->writeln($path);
        }
    }

    /**
     * @return int[]
     */
    private function getMunicipalityIds(string $filename, SymfonyStyle $io, bool $verbose = false): array
    {
        $reader = new \XMLReader();
        if ($verbose) {
            $io->comment(\sprintf('Otevírám "%s"', $filename));
        }
        if (false === $reader->open($filename)) {
            throw new \InvalidArgumentException(\sprintf('Soubor %s nelze otevřít!', $filename));
        }

        $ids = [];

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

            if (\XMLReader::ELEMENT === $reader->nodeType) {
                if ($this->municipalityConverter->accepts($reader)) {
                    $data = $this->municipalityConverter->convert($reader)->getData();
                    if (!empty($data['obec_kod']) && \is_scalar($data['obec_kod'])) {
                        $ids[] = (int) $data['obec_kod'];
                    }
                }
            }
        }

        $reader->close();

        return $ids;
    }

    private function printDiffDataList(SymfonyStyle $io, string $startDate, bool $verbose = false): void
    {
        $filename = str_replace('%datum_od%', $startDate, $this->changeDataPathPattern);
        if ($verbose) {
            $io->comment(\sprintf('Stahuji "%s"', $filename));
        }
        $content = $this->downloader->getFile($filename);

        if (false === $content) {
            throw new \RuntimeException(\sprintf('Nepodařilo se stáhnout soubor "%s"', $filename));
        }

        $io->write((string) $content);
    }
}
