<?php declare(strict_types=1);

namespace Newland\NeosCommon\Service;

use Neos\Flow\I18n;
use Neos\Flow\I18n\Xliff\Service\XliffReader;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Package\PackageManager;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Finder\SplFileInfo;

class LocalizationService
{
    private const TRANSLATION_PATH = 'resource://%s/Private/Translations';

    /**
     * @var XliffReader
     * @Flow\Inject
     */
    protected $xliffReader;

    /**
     * @var I18n\Service
     * @Flow\Inject()
     */
    protected $service;

    /**
     * @var PackageManager
     * @Flow\Inject()
     */
    protected $packageManager;

    public function listAvailableLanguages(string $package): array
    {
        $languages = [];
        $path = $this->translationPath($package);

        foreach (glob($path . '*', GLOB_ONLYDIR) as $dir) {
            $languages[] = str_replace($path, '', $dir);
        }

        return $languages;
    }

    public function listAvailableTranslationSources(string $package): array
    {
        $finder = (new Finder())
            ->files()
            ->in($this->translationPath($package))
            ->name('*.xlf');

        $sources = [];
        foreach ($finder as $file) {
            /** @var SplFileInfo $file */
            $pathParts = explode('/', $file->getRelativePathname());
            array_shift($pathParts);
            $sources[] = str_replace('.xlf', '', implode('/', $pathParts));
        }

        return array_unique($sources);
    }

    public function getTranslationsForSource(string $package, string $source, ?string $language = null): array
    {
        $filename = $this->getXliffFileName($package, $source, $language);
        if (!$filename) {
            return [];
        }

        $xliff = $this->readXliff($filename);
        if (!$xliff) {
            return [];
        }

        return $this->extractTranslations($xliff);
    }

    private function getXliffFileName(string $package, string $source, ?string $language): ?string
    {
        $locale = null;
        if ($language) {
            $locale = new I18n\Locale($language);
        }

        [ $filename ] = $this->service->getXliffFilenameAndPath(
            sprintf(self::TRANSLATION_PATH, $package),
            $source,
            $locale
        );

        return $filename ?: null;
    }

    private function readXliff(string $filename): ?\DOMElement
    {
        $node = null;
        $this->xliffReader->readFiles(
            $filename,
            function (\XMLReader $reader) use (&$node) {
                $node = $reader->expand();
            }
        );

        return ($node instanceof \DOMElement) ? $node : null;
    }

    private function extractTranslations(\DOMElement $node): array
    {
        $translations = [];
        foreach ($node->getElementsByTagName('trans-unit') as $transUnit) {
            /** @var \DOMElement $transUnit */
            $id = $transUnit->getAttribute('id');
            $content = '';
            foreach ($transUnit->childNodes as $child) {
                if ($child->nodeName === 'source' || $child->nodeName === 'target') {
                    $content = $child->textContent;
                    break;
                }
            }

            $translations[$id] = $content;
        }

        return $translations;
    }

    private function translationPath(string $package): string
    {
        $base = $this->packageManager->getPackage($package)->getPackagePath();
        return $base . 'Resources/Private/Translations/';
    }
}
