<?php
namespace Newland\Toubiz\Poi\Neos\Service;

/*
 * This file is part of the "toubiz-poi-neos" package.
 *
 * For the full copyright and license information, please read the
 * LICENSE.txt file that was distributed with this source code.
 */

use Neos\Flow\Annotations as Flow;
use Neos\ContentRepository\Domain\Model\Node;
use Neos\Flow\I18n\Locale;
use Neos\Flow\I18n\Service;
use Neos\Flow\I18n\Translator;
use Neos\Utility\Arrays;
use Newland\Toubiz\Poi\Neos\Domain\Model\Tag;
use Newland\Toubiz\Poi\Neos\Domain\Repository\TopicRepository;

/**
 * Class TagTranslationService
 *
 * @package Newland\Toubiz\Poi\Neos\Service
 * @Flow\Scope("singleton")
 */
class TagTranslationService
{
    /**
     * @var Node
     */
    protected $node;

    /**
     * @var Translator
     * @Flow\Inject
     */
    protected $translator;

    /**
     * @var Service
     * @Flow\Inject
     */
    protected $localizationService;

    /**
     * @var array
     * @Flow\InjectConfiguration(package="Newland.Toubiz.Poi.Neos", path="filter.options")
     */
    protected $options;

    /**
     * @var Locale
     */
    private $locale;

    public function getTitle(Tag $tag, Node $node): string
    {
        $this->parseLocaleFromNode($node);

        if (!empty($tag->getTitle())) {
            return $tag->getTitle();
        }

        switch ($tag->getAttributeType()) {
            case Tag::TYPE_RANGE:
                return $this->getRangeTitle($tag);
            case Tag::TYPE_STRING:
                return $this->getValueTitle($tag);
            default:
                return $this->getAttributeTitle($tag->getAttribute());
        }
    }

    public function translate(string $id, array $parsedLanguages = []): string
    {
        /** @var string|null $translation */
        $translation = $this->translator->translateById(
            $id,
            [],
            null,
            $this->locale,
            'Models/Topic',
            'Newland.Toubiz.Poi.Neos'
        );

        if ($translation !== null) {
            return $translation;
        }

        $localeChain = $this->localizationService->getLocaleChain($this->locale);
        $locale = next($localeChain);
        if ($locale !== false && in_array($locale->getLanguage(), $parsedLanguages, true) === false) {
            $this->setLocale($locale);
            $parsedLanguages[] = $locale->getLanguage();
            return $this->translate($id, $parsedLanguages);
        }

        return $id;
    }

    public function setLocale(Locale $locale): void
    {
        $this->locale = $locale;
    }

    /**
     * For manual translation we need to find the current language.
     */
    private function parseLocaleFromNode(Node $node): void
    {
        // This only needs to be parsed once because the locale should not change during a single request.
        if ($this->locale === null) {
            $dimensions = $node->getDimensions();
            if (isset($dimensions['language'])
                && is_array($dimensions['language'])
                && !empty($dimensions['language'])
            ) {
                $this->setLocale(new Locale($dimensions['language'][0]));
            }
        }
    }

    private function getAttributeTitle(string $attribute): string
    {
        return $this->translate(TopicRepository::TYPE_ATTRIBUTE . '.' . str_replace(':', '.', $attribute));
    }

    private function getUnit(string $attribute): string
    {
        return Arrays::getValueByPath($this->options, $attribute . '.unit') ?? '';
    }

    private function applyFactor(string $attribute, float $value): string
    {
        $factor = Arrays::getValueByPath($this->options, $attribute . '.factor') ?? 1;
        $steps = Arrays::getValueByPath($this->options, $attribute . '.steps') ?? 1;
        $numberOfDecimals = 1 / $steps;
        $precision = (int) log($numberOfDecimals, 10);
        return number_format($value * $factor, $precision, '.', '');
    }

    private function getRangeTitle(Tag $tag): string
    {
        $title = $this->getAttributeTitle($tag->getAttribute());

        if (preg_match('/(?<min>\d+)(\.\d+)?;(?<max>\d+)(\.\d+)?/', $tag->getValue(), $matches)) {
            $title = sprintf(
                '%s %s - %s%s',
                $this->getAttributeTitle($tag->getAttribute()),
                $this->applyFactor($tag->getAttribute(), (float) $matches['min']),
                $this->applyFactor($tag->getAttribute(), (float) $matches['max']),
                $this->getUnit($tag->getAttribute())
            );
        }

        return $title;
    }

    private function getValueTitle(Tag $tag): string
    {
        return sprintf(
            '%s %s',
            $this->getAttributeTitle($tag->getAttribute()),
            $this->getAttributeTitle($tag->getAttribute() . '.' . $tag->getValue())
        );
    }
}
