<?php

declare(strict_types=1);

/*
 * Chill is a software for social workers
 *
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

namespace Chill\MainBundle\Templating;

use Symfony\Bridge\Twig\Extension\RoutingExtension;
use Symfony\Component\HttpFoundation\RequestStack;
use Twig\Extension\AbstractExtension;
use Twig\Node\Node;
use Twig\TwigFilter;
use Twig\TwigFunction;

/**
 * Provides function to build path with returnPath.
 *
 * The logic of the function is based on the original routing extension.
 */
class ChillTwigRoutingHelper extends AbstractExtension
{
    /**
     * @var RoutingExtension
     */
    protected $originalExtension;

    /**
     * @var RequestStack
     */
    protected $requestStack;

    public function __construct(
        RequestStack $requestStack,
        RoutingExtension $originalExtension,
    ) {
        $this->requestStack = $requestStack;
        $this->originalExtension = $originalExtension;
    }

    public function getFilters()
    {
        return [
            new TwigFilter('chill_return_path_label', $this->getLabelReturnPath(...)),
        ];
    }

    public function getFunctions()
    {
        return [
            new TwigFunction('chill_return_path_or', $this->getReturnPathOr(...), ['is_safe_callback' => $this->isUrlGenerationSafe(...)]),
            new TwigFunction('chill_path_add_return_path', $this->getPathAddReturnPath(...), ['is_safe_callback' => $this->isUrlGenerationSafe(...)]),
            new TwigFunction('chill_path_forward_return_path', $this->getPathForwardReturnPath(...), ['is_safe_callback' => $this->isUrlGenerationSafe(...)]),
            new TwigFunction('chill_path_force_return_path', $this->getPathForceReturnPath(...), ['is_safe_callback' => $this->isUrlGenerationSafe(...)]),
        ];
    }

    /**
     * Build a URL with a forced returnPath parameter.
     *
     * @param string      $forcePath  the forced path to return to
     * @param string      $name       the name of the route
     * @param array       $parameters additional parameters for the URL
     * @param bool        $relative   whether the URL should be relative
     * @param string|null $label      optional label for the return path
     *
     * @return string the generated URL
     */
    public function getPathForceReturnPath(string $forcePath, string $name, array $parameters = [], $relative = false, $label = null): string
    {
        $params = [...$parameters, 'returnPath' => $forcePath];

        if (null !== $label) {
            $params['returnPathLabel'] = $label;
        }

        return $this->originalExtension->getPath($name, $params, $relative);
    }

    public function getLabelReturnPath($default)
    {
        $request = $this->requestStack->getCurrentRequest();

        return $request->query->get('returnPathLabel', null) ?? $default;
    }

    /**
     * Build an url with a returnPath parameter to current page.
     *
     * @param string     $name
     * @param array      $parameters
     * @param bool       $relative
     * @param mixed|null $label
     *
     * @return string
     */
    public function getPathAddReturnPath($name, $parameters = [], $relative = false, $label = null)
    {
        $request = $this->requestStack->getCurrentRequest();

        $parameters['returnPath'] = $request->getRequestUri();

        if ($label) {
            $parameters['returnPathLabel'] = $label;
        }

        return $this->originalExtension->getPath($name, $parameters, $relative);
    }

    /**
     * Build an url with a returnPath parameter to current page.
     *
     * @param string $name
     * @param array  $parameters
     * @param bool   $relative
     *
     * @return string
     */
    public function getPathForwardReturnPath($name, $parameters = [], $relative = false)
    {
        $request = $this->requestStack->getCurrentRequest();

        if ($request->query->has('returnPath')) {
            $parameters['returnPath'] = $request->query->get('returnPath');
        }

        return $this->originalExtension
            ->getPath(
                $name,
                $parameters,
                $relative
            );
    }

    /**
     * Return the return path if it exists, or generate the path if not.
     *
     * @param string $name
     * @param array  $parameters
     * @param bool   $relative
     *
     * @return string
     */
    public function getReturnPathOr($name, $parameters = [], $relative = false)
    {
        $request = $this->requestStack->getCurrentRequest();

        if ($request->query->has('returnPath')) {
            return $request->query->get('returnPath');
        }

        return $this->originalExtension->getPath($name, $parameters, $relative);
    }

    public function isUrlGenerationSafe(Node $argsNode)
    {
        return $this->originalExtension->isUrlGenerationSafe($argsNode);
    }
}
