<?php


namespace AutoAdmin\Bundle\Seo\Extractor;


use AutoAdmin\Bundle\Content\Configuration\ConfigManager;
use AutoAdmin\Bundle\Content\Context\ContentContext;
use AutoAdmin\Bundle\Content\Entity\Page;
use AutoAdmin\Bundle\Content\Model\TranslatableInterface;
use AutoAdmin\Bundle\Content\Model\TranslationInterface;
use AutoAdmin\Bundle\Content\Routing\ContentRouter;
use AutoAdmin\Bundle\Content\Site\SiteContextHelper;
use AutoAdmin\Bundle\Seo\Model\Alternate;
use AutoAdmin\Bundle\Seo\Model\Header;
use AutoAdmin\Bundle\Seo\Model\Meta;
use AutoAdmin\Bundle\Seo\Model\SeoContentTranslationInterface;
use AutoAdmin\Bundle\Seo\Model\SeoPage;
use Exception;
use Symfony\Component\HttpFoundation\Request;

class SeoContentExtractor extends AbstractExtractor
{
    public function __construct(
        protected ConfigManager $config,
        protected ContentRouter $contentRouter,
        protected SiteContextHelper $contextHelper,
    ) {
    }

    public function supports($subject, $route): bool
    {
        return $this->isTranslatable($subject)
            || $this->isTranslation($subject)
            || $this->isUntranslatedContent($subject);
    }

    protected function isTranslation(object $subject): bool
    {
        return $subject instanceof TranslationInterface && $subject->getTranslatable() !== null;
    }


    protected function isTranslatable(object $subject): bool
    {
        return $subject instanceof TranslatableInterface && $subject->getCurrentTranslation() !== null;
    }

    protected function isUntranslatedContent(object $subject): bool
    {
        return $subject instanceof SeoContentTranslationInterface;
    }

    public function extract(Request $request, $subject): SeoPage
    {
        if ($this->isTranslatable($subject)) {
            $translatable = $subject;
            $translation = $subject->getCurrentTranslation();
        } elseif ($this->isTranslation($subject)) {
            $translation = $subject;
            $translatable = $subject->getTranslatable();
        } elseif ($this->isUntranslatedContent($subject)) {
            // untranslated content, self act as translatable and translation
            $translatable = $translation = $subject;
        } else {
            throw new Exception('Main content should implement some kind of SEO');
        }

        $seo = new SeoPage();
        $seoMetadata = $translation->getSeoMetadata();

        if ($seoMetadata->getTitle() && ($title = $this->sanitize($seoMetadata->getTitle()))) {
            $seo->setTitle($title);
        }

        if ($seoMetadata->getDescription() && ($desc = $this->sanitize($seoMetadata->getDescription()))) {
            $seo->addMeta(new Meta('description', $desc));
        }

        if (!empty($metas = $seoMetadata->getMetas())) {
            foreach ($metas as $meta => $value) {
                $seo->addMeta(new Meta($meta, $value));
            }
        }

        // canonical
        if (!empty($seoMetadata->getCanonicalOverride())) {
            $seo->setCanonical($seoMetadata->getCanonicalOverride());
        } else {
            $seo->setCanonical(strtok($request->getUri(), '?'));
        }

        // alternates
        $alternates = [];
        if ($translatable instanceof TranslatableInterface) {
            $route = $request->get('_route');
            $action = $this->config->getRouteAction($route);

            // null action usually mean we are out of content routes, for example in contact controller
            // use the main content if page is provided and override route and action
            if (
                $action === null
                && ($refl = new \ReflectionClass($translatable))
                && $refl->isSubclassOf(Page::class)
            ) {
                $route = 'page';
                $action = 'view';
            }

            /** @var TranslationInterface $translation */
            foreach ($translatable->getTranslations() as $translation) {
                $locale = $translation->getLocale();
                // check if route is localized (route name ending with locale part: .fr, .en)
                // if that's the case, get right locale
                if (
                    preg_match("/(.+?)(\.([a-z]{2}))/", $route, $matches)
                    && !empty($localeCode = $matches[3])
                ) {
                    $locale = $this->contextHelper->getSiteLocaleForCode($localeCode);
                }

                $domain = $this->contextHelper->getDomainByLocale($translatable->getSite(), $locale);
                $url = $this->contentRouter->generate(
                    $translatable,
                    $action,
                    ['_locale' => $locale->getCode(),],
                    ['host' => $domain->getDomain(),]
                );

                $alternates[] = new Alternate($locale, $url);
            }
        }
        $seo->setAlternates($alternates);

        // noindex/nofollow
        $xRobotsTag = [];
        if ($seoMetadata->isNoIndex() === true) {
            $xRobotsTag[] = 'noindex';
        }

        if ($seoMetadata->isNoFollow() === true) {
            $xRobotsTag[] = 'nofollow';
        }

        if ($xRobotsTag) {
            $seo->addResponseHeader(new Header('X-Robots-Tag', implode(', ', $xRobotsTag)));
        }

        return $seo;
    }
}