<?php

namespace AutoAdmin\Bundle\Content\Site;

use AutoAdmin\Bundle\Content\Context\SiteContext;
use AutoAdmin\Bundle\Content\Entity\Domain;
use AutoAdmin\Bundle\Content\Entity\Locale;
use AutoAdmin\Bundle\Content\Frontend\FrontendUrlChecker;
use AutoAdmin\Bundle\Content\Repository\DomainRepository;
use AutoAdmin\Bundle\Content\Translation\EntityTranslator;
use Doctrine\Persistence\ManagerRegistry;
use RuntimeException;
use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

#[Autoconfigure(bind: [
    '$appEnv' => "%env(string:APP_ENV)%",
])]
class SiteMatcher
{
    public function __construct(
        protected ManagerRegistry $doctrine,
        protected EntityTranslator $entityTranslator,
        protected FrontendUrlChecker $frontendUrlChecker,
        protected HostLocaleFinder $hostLocaleFinder,
        protected $appEnv,
    ) {
    }

    public function matchSite(Request $request, SiteContextHelper $contextHelper): void
    {
        $uri = $request->getRequestUri();

        $parsedUrl = parse_url($uri);
        if (isset($parsedUrl['path']) && stripos($parsedUrl['path'], '//') !== false) {
            throw new NotFoundHttpException('Double slash in url');
        }

        // ignore /media/*** urls to avoid trying domain matching on static files
        if (
            str_starts_with($uri, "/media/")
            && !str_starts_with($uri, "/media/cache/resolve/")
        ) {
            throw new NotFoundHttpException();
        }

        /** @var DomainRepository $domainRepo */
        $domainRepo = $this->doctrine->getRepository(Domain::class);
        $localeRepo = $this->doctrine->getRepository(Locale::class);

        $allowedLocales = $localeRepo->findAll();
        $allowedLocaleCodes = array_map(fn(Locale $locale) => $locale->getCode(), $allowedLocales);

        if ($request->attributes->has('_locale')) {
            $localeValue = $request->attributes->get('_locale');
            if (!in_array($localeValue, $allowedLocaleCodes)) {
                throw new NotFoundHttpException('Bad locale format');
            }
        }

        $matchedLocale = null;

        // get locale from route
        if (null !== $request->get('_admin_locale')) {
            $matchedLocale = $request->get('_admin_locale');
        }

        if (null !== $request->get('_locale')) {
            $matchedLocale = $request->get('_locale');
        }

        // get locale using host
        if (is_null($matchedLocale)) {
            $routeParams = $request->get('_route_params');
            if (!empty($routeParams['_locale'])) {
                $matchedLocale = $routeParams['_locale'];
            }
        }

        $host = $request->getHost();
        if (is_null($matchedLocale)) {
            $matchedLocale = $this->hostLocaleFinder->findDefaultLocale($host);
        }

        // match domain with locale
        $domain = $domainRepo->findMatchingHost($host, $matchedLocale);

        // allow to match another domain if another locale (using diff. domain) is chosen in admin's locale selector
        if (empty($domain) && !$this->frontendUrlChecker->isFrontUrl()) {
            $domain = $domainRepo->findMatchingHostAnyLocale($host);
        }

        if (empty($domain)) {
            throw new RuntimeException('Could not find matching domain.');
        }

        $site = $domain->getSite();

        $context = new SiteContext();
        $context->setSite($site);

        /** @var Locale|null $locale */
        $locale = $localeRepo->findOneBy(['code' => $matchedLocale]);
        $context->setLocale($locale);

        if ($context->getSite() === null || $context->getLocale() === null) {
            throw new RuntimeException('Could not match site entity with available information.');
        }

        // init our context on main helper
        $contextHelper->loadContext($context);

        // finally, set locale on related services
        $this->setupTranslatorLocale($context->getLocale());
    }

    protected function setupTranslatorLocale(Locale $locale): void
    {
        $this->entityTranslator->setLocale($locale);
    }
}
