<?php

namespace AutoAdmin\Bundle\Seo\Sitemap;

use AutoAdmin\Bundle\Content\Configuration\ConfigManager;
use AutoAdmin\Bundle\Content\Model\CreatedAtInterface;
use AutoAdmin\Bundle\Content\Model\TranslatableInterface;
use AutoAdmin\Bundle\Content\Model\UpdatedAtInterface;
use AutoAdmin\Bundle\Content\Repository\AbstractContentRepository;
use AutoAdmin\Bundle\Content\Routing\ContentRouter;
use AutoAdmin\Bundle\Content\Site\SiteContextHelper;
use AutoAdmin\Bundle\Seo\Model\SeoContentTranslationInterface;
use AutoAdmin\Bundle\Seo\Sitemap\Model\SitemapUrl;
use DateTime;
use Doctrine\Persistence\ManagerRegistry;

/**
 * Try to generate sitemap from content entity config only
 */
class ConfigSitemapBuilder implements SitemapBuilderInterface
{
    public function __construct(
        protected readonly ConfigManager $configManager,
        protected readonly ManagerRegistry $doctrine,
        protected readonly SiteContextHelper $siteContextHelper,
        protected readonly ContentRouter $contentRouter,
    )
    {
    }

    public function getSections(): array
    {
        $sections = [];

        $sitemapConfig = $this->configManager->getSitemap();

        foreach ($sitemapConfig['config'] as $sectionSlug => $config) {
            if (!in_array($sectionSlug, $sections) && $config['disable'] === false) {
                $sections[] = $sectionSlug;
            }
        }

        foreach ($sitemapConfig['routes'] as $sectionSlug => $routeConfigs) {
            if (!in_array($sectionSlug, $sections)) {
                $sections[] = $sectionSlug;
            }
        }

        return $sections;
    }

    public function getUrls(?string $section = null): array
    {
        $safeUrlGen = ($cfg = $this->configManager->get('sitemap')) && $cfg['safe_url_generation'] === true;

        $urls = [];

        // after builders, generate content sitemap from config
        $sitemapConfig = $this->configManager->getSitemap();
        foreach ($sitemapConfig['config'] as $sectionSlug => $sectionConfig) {
            if ($sectionConfig['disable'] === true || $sectionSlug !== $section) {
                continue;
            }

            foreach ($sectionConfig['contents'] as $contentSlug) {
                $contentCfg = $this->configManager->getContentConfigForSlug($contentSlug);
                $contentClass = $contentCfg['class'];

                if ($contentCfg['sitemap']['disable'] === true) {
                    continue;
                }

                $contents = $this->resolveContents($contentClass);
                $urlAction = !empty($contentCfg['action_chain']) ? 'chain' : 'view';
                $changeFreq = $contentCfg['sitemap']['changefreq'];

                foreach ($contents as $content) {
                    $tr = $content instanceof TranslatableInterface ? $content->getCurrentTranslation() : null;
                    $date = new DateTime();
                    if ($content instanceof UpdatedAtInterface) {
                        $date = $content->getUpdatedAt();
                    } elseif ($content instanceof CreatedAtInterface) {
                        $date = $content->getCreatedAt();
                    }

                    if ($content instanceof SeoContentTranslationInterface) {
                        $seoHolder = $content;
                    } elseif ($tr instanceof SeoContentTranslationInterface) {
                        $seoHolder = $tr;
                    } else {
                        $seoHolder = null;
                    }

                    $hideFromSitemap = false;
                    $canonicalOverride = '';
                    $differentCanonicalDomain = false;
                    if ($seoHolder) {
                        $hideFromSitemap = ($seoHolder->getSeoMetadata()?->isHideFromSitemap() === true);

                        $canonicalOverride = $seoHolder->getSeoMetadata()?->getCanonicalOverride();
                        if (!empty($canonicalOverride)) {
                            $relativeOverride = str_starts_with((string)$canonicalOverride, '/');
                            // if override is not relative,
                            // then domain should be provided by override and checked against site context
                            if (!$relativeOverride) {
                                $parsedCanonical = parse_url($canonicalOverride);

                                // we need a host if canonical is not relative, otherwise syntax is not good
                                if (empty($parsedCanonical['host'])) {
                                    continue;
                                }

                                if (
                                    $parsedCanonical['host'] === $this->siteContextHelper->getDomain()->getDomain()
                                    && $parsedCanonical['scheme']
                                ) {
                                    $schemeHost = $parsedCanonical['scheme'] . '://' . $parsedCanonical['host'];
                                    $canonicalOverride = str_replace($schemeHost, '', $canonicalOverride);
                                } else {
                                    $differentCanonicalDomain = true;
                                }
                            }
                        }
                    }

                    if ($hideFromSitemap || $differentCanonicalDomain) {
                        continue;
                    }

                    if (!empty($canonicalOverride)) {
                        $url = $canonicalOverride;
                    } else {
                        if ($safeUrlGen) {
                            $url = $this->contentRouter->generateSafe($content, $urlAction);
                        } else {
                            $url = $this->contentRouter->generate($content, $urlAction);
                        }
                    }

                    $sitemapUrl = new SitemapUrl($url, $date, $changeFreq);
                    $urls[] = $sitemapUrl;
                }
            }
        }

        foreach ($sitemapConfig['routes'] as $sectionSlug => $routeConfigs) {
            if ($sectionSlug !== $section) {
                continue;
            }

            foreach ($routeConfigs as $routeConfig) {
                $route = $routeConfig['route'];
                [$contentSlug, $action] = explode('_', $route, 2);
                $param = $routeConfig['param'];
                if (!empty($param['slugAdmin'])) {
                    $url = $this->contentRouter->generate($contentSlug, $action, $param ?? []);
                } else {
                    $url = $this->contentRouter->getRouter()->generate($route, $param ?? []);
                }

                $sitemapUrl = new SitemapUrl($url, new DateTime(), SitemapUrl::DEFAULT_CHANGE_FREQ);
                $urls[] = $sitemapUrl;
            }
        }

        return $urls;
    }

    public function resolveContents(string $class): mixed
    {
        $repo = $this->doctrine->getRepository($class);

        if ($repo instanceof AbstractContentRepository) {
            $qb = $repo->createAutoContentQueryBuilder();
        } else {
            $qb = $repo->createQueryBuilder();
        }

        return $qb->getQuery()->getResult();
    }
}
