<?php

declare(strict_types=1);

namespace Zab\ZabSite\ViewHelpers;

use Psr\Http\Message\ServerRequestInterface;
use Zab\ZabSite\Search\FuzzyWordMatcher;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;

/**
 * Provides a "Did you mean?" suggestion for a search term that returned no results.
 * Exposes an array {word, url} via the `as` variable.
 *
 * Usage in Fluid:
 *   <zab:fuzzySearchSuggestion sword="{searchParams.sword}" searchParams="{searchParams}" as="fuzzy">
 *     <f:if condition="{fuzzy.word}">
 *       <p>Meinten Sie: <a href="{fuzzy.url}">{fuzzy.word}</a>?</p>
 *     </f:if>
 *   </zab:fuzzySearchSuggestion>
 */
class SearchFuzzySuggestionViewHelper extends AbstractViewHelper
{
    protected $escapeOutput = false;

    public function initializeArguments(): void
    {
        $this->registerArgument('sword', 'string', 'The search term to find a suggestion for', true);
        $this->registerArgument('searchParams', 'array', 'Current search params from the plugin', false, []);
        $this->registerArgument('as', 'string', 'Variable name to expose {word, url}', true);
        $this->registerArgument('maxDistance', 'int', 'Maximum Levenshtein distance (default: 2)', false, 2);
    }

    public function render(): string
    {
        $sword = trim((string)($this->arguments['sword'] ?? ''));
        $searchParams = (array)($this->arguments['searchParams'] ?? []);
        $as = (string)$this->arguments['as'];
        $maxDistance = (int)$this->arguments['maxDistance'];

        $result = null;

        if ($sword !== '') {
            $matcher = GeneralUtility::makeInstance(FuzzyWordMatcher::class);
            $suggestion = $matcher->findClosestWord($sword, $maxDistance);

            if ($suggestion !== null) {
                $request = $this->renderingContext->getRequest();
                $result = [
                    'word' => $suggestion,
                    'url'  => $this->buildSuggestionUrl($suggestion, $searchParams, $request),
                ];
            }
        }

        $this->renderingContext->getVariableProvider()->add($as, $result);
        $content = $this->renderChildren();
        $this->renderingContext->getVariableProvider()->remove($as);

        return (string)$content;
    }

    private function buildSuggestionUrl(string $suggestion, array $searchParams, ServerRequestInterface $request): string
    {
        // Take all current search params, replace sword and reset pagination pointer
        $mergedSearch = array_merge($searchParams, [
            'sword'   => $suggestion,
            'pointer' => 0,
        ]);

        $queryParams = [
            'tx_indexedsearch_pi2' => [
                'action' => 'search',
                'search' => $mergedSearch,
            ],
        ];

        $uri = $request->getUri();

        return $uri->getScheme() . '://' . $uri->getAuthority() . $uri->getPath()
            . '?' . http_build_query($queryParams);
    }
}
