<?php

namespace AutoAdmin\Bundle\Content\Routing;

use AutoAdmin\Bundle\Content\Model\TranslatableInterface;
use AutoAdmin\Bundle\Content\Translation\EntityTranslator;
use AutoAdmin\Bundle\Core\Helper\DoctrineHelper;
use Traversable;

class AutoRouterManager
{
    public function __construct(
        Traversable $guessers,
        protected EntityTranslator $translator,
        /* @var $routers array<ContentAutoRouterInterface> */
        protected array $routers = [],
        protected array $refs = [],
        protected array $objects = [],
    ) {
        $this->routers = array_map([$this, 'addRouter'], iterator_to_array($guessers));
    }

    public function generate(
        ContentRouter $contentRouter,
        object $subject,
        ?string $route,
        array $parameters = [],
        array $options = []
    ) {
        $contentKey = $this->createKey($subject, $parameters, $options);
        if (array_key_exists($contentKey, $this->refs)) {
            return $this->refs[$contentKey];
        }

        $url = null;
        foreach ($this->routers as $router) {
            if ($router->supports($subject)) {
                $url = $router->buildUrl($contentRouter, $subject, $route, $parameters, $options);
                break;
            }
        }

        if ($subject && !empty($contentKey)) {
            $this->refs[$contentKey] = $url;
        }

        return $url;
    }

    /**
     * @param object $content
     * @param array $parameters
     * @param array $options
     * @return string
     *
     * We were previously using "id and class" or "spl object id" for indexes. However, this wasn't working with object mutations,
     * also tried php serialize, wasn't doing good with closures
     * also tried symfony serializer, wasn't fast enough (probably too much info kept/processed)
     *
     * We can now use mutations and cloning to generate multiple urls from one given object (cf. AL cocoons)
     */
    protected function createKey(object $content, array $parameters = [], array $options = []): string
    {
        $sep = '~$3P~';
        $keyParts = [];

        $contentKeyPart = DoctrineHelper::getClass($content).'#'.$content->getId();
        $keyParts[] = $contentKeyPart;

        $localeKeyPart = 'locale:';
        if (!empty($l = $options['locale'] ?? null)) {
            $localeKeyPart .= $l;
        } elseif (!empty($l = $parameters['_locale'] ?? null)) {
            $localeKeyPart .= $l;
        } elseif ($content instanceof TranslatableInterface && $content->getCurrentTranslation()) {
            $localeKeyPart .= $content->getCurrentTranslation()->getLocale();
        } else {
            $localeKeyPart .= 'none';
        }
        $keyParts[] = $localeKeyPart;

        $parametersSignatureKeyPart = http_build_query($parameters);
        $keyParts[] = $parametersSignatureKeyPart;

        $phpKeyPart = spl_object_id($content); // permit to use "clone" to force cache ignoring

        if (!in_array($content, $this->objects, true)) {
            $this->objects[] = $content; // Allow the full usage of spl_object_id() by preventing the object from being garbage collected
        }

        $keyParts[] = $phpKeyPart;

        return implode($sep, $keyParts);
    }

    protected function addRouter(ContentAutoRouterInterface $router): ContentAutoRouterInterface
    {
        return $router;
    }
}
