<?php

namespace AutoAdmin\Bundle\Seo\EventListener;

use AutoAdmin\Bundle\Seo\Entity\Redirect;
use AutoAdmin\Bundle\Seo\Repository\RedirectRepository;
use Doctrine\Persistence\ManagerRegistry;
use Exception;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\RequestEvent;

class RedirectEventListener
{
    private ManagerRegistry $doctrine;

    public function __construct(ManagerRegistry $doctrine)
    {
        $this->doctrine = $doctrine;
    }

    public function onKernelRequest(RequestEvent $event): void
    {
        $this->searchMatchingRedirect($event);
    }

    protected function searchMatchingRedirect(RequestEvent $event): void
    {
        if (!$event->isMainRequest()) {
            return;
        }

        $request = $event->getRequest();
        $uri = $request->getRequestUri();

        if (
            stripos($uri, '/_wdt/') === 0
            || stripos($uri, '/admin/') === 0 // not ideal, would prefer to keep admin url modifiable
        ) {
            return;
        }

        // TODO: optimize speed by caching redirects data to avoid db query
        /** @var RedirectRepository $repo */
        $repo = $this->doctrine->getRepository(Redirect::class);
        /** @var Redirect|null $found */
        $found = $repo->findOneBy(['origin' => $uri]);

        if (!$found) {
            $found = $repo->findOneByOriginLike($uri);
            if (!$found) {
                return;
            }
        }

        try {
            $destination = $this->handleDestination($found, $uri);
        } catch (Exception) {
            return;
        }

        $found->setCounter($found->getCounter() + 1);
        $entityManager = $this->doctrine->getManager();
        $entityManager->persist($found);
        $entityManager->flush();

        $response = new RedirectResponse(
            $destination,
            $found->getType() ?: Response::HTTP_FOUND,
            ['X-Custom-Redirection' => '1']
        );

        $event->setResponse($response);
    }

    private function handleDestination(Redirect $found, string $uri): string
    {
        $destination = $found->getDestination();
        $origin = $found->getOrigin();
        // if destination has "**" (redirect multiple) as the end of slug
        if (str_ends_with($destination, '**')) {
            $origin = str_replace('**', '', $origin);

            // get end of origin URL to pass to destination
            $originEnd = substr($uri, stripos($uri, $origin) + strlen($origin));

            // add to destination
            $destination = str_replace('**', $originEnd, $destination);
        }

        // Format regex
        $originRegex = preg_replace('#(\*[^*]+\*)#', '(.+)', $origin);
        $originRegex = str_replace('/', '\/', $originRegex);
        $originRegex = str_replace('?', '\?', $originRegex);

        // Get matches with regex
        if (!preg_match("#$originRegex#", $uri, $matches)) {
            throw new Exception('No match found');
        }

        // Assign matches to destination
        foreach ($matches as $key => $match) {
            $destination = str_replace("*$key*", $match, $destination);
        }

        return $destination;
    }
}
