<?php

namespace AutoAdmin\Bundle\Seo\Redirect;

use AutoAdmin\Bundle\Content\Entity\Domain;
use AutoAdmin\Bundle\Content\Frontend\FrontendUrlChecker;
use AutoAdmin\Bundle\Seo\Entity\Redirect;
use AutoAdmin\Bundle\Seo\Repository\RedirectRepository;
use Doctrine\Persistence\ManagerRegistry;
use Exception;
use RuntimeException;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class RedirectService
{
    public function __construct(
        protected ManagerRegistry $doctrine,
        protected FrontendUrlChecker $frontendUrlChecker,
    )
    {
    }

    public function getRedirect(Request $request, bool $rootRedirect = false): ?Redirect
    {
        $uri = $request->getRequestUri();

        if ($this->frontendUrlChecker->isFrontUrl($request)) {
            return null;
        }

        /** @var RedirectRepository $repo */
        $repo = $this->doctrine->getRepository(Redirect::class);

        if ($rootRedirect === false) {
            $host = $request->getHost();
            $domain = $this->doctrine->getRepository(Domain::class)->findOneBy(['domain' => $host]);
            if (empty($domain)) {
                throw new RuntimeException('Could not find matching domain.');
            }

            $site = $domain->getSite();
            if (empty($site)) {
                throw new RuntimeException('Could not find matching site.');
            }

            // TODO: optimize speed by caching redirects data to avoid db query
            $found = $repo->findOneByExactOrigin($uri, false, $site);
            if (!$found) {
                $found = $repo->findOneByOriginLike($uri, false, $site);
            }
        } else {
            // TODO: optimize speed by caching redirects data to avoid db query
            $found = $repo->findOneByExactOrigin($uri, true);
            if (!$found) {
                $found = $repo->findOneByOriginLike($uri, true);
            }
        }

        if (!$found) {
            return null;
        }

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

        return $found;
    }

    public function getRedirectResponse(Request $request, bool $rootRedirect = false): ?Response
    {
        $found = $this->getRedirect($request, $rootRedirect);
        if (!$found) {
            return null;
        }

        $uri = $request->getRequestUri();
        try {
            $destination = $this->replacePlaceholders($found, $uri);
        } catch (Exception) {
            return null;
        }

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

        return $response;
    }

    private function replacePlaceholders(Redirect $found, string $uri): string
    {
        $destination = $found->getDestination();
        $origin = $found->getOrigin();

        // Format regex
        $originRegex = preg_replace('#(\*[^*]+\*)#', '(.+)', $origin);
        $originRegex = preg_replace('#(\*\*)#', '(.+)', $originRegex);
        $originRegex = preg_replace('#(\*)#', '(.+)', $originRegex);
        $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);
            if (stripos($destination, '**') !== false) {
                $destination = str_replace("**", $matches[1], $destination);
            }
        }

        return $destination;
    }
}
