<?php


namespace AutoAdmin\Bundle\Content\EventListener;


use AutoAdmin\Bundle\Content\Configuration\ConfigManager;
use AutoAdmin\Bundle\Content\Entity\Content;
use AutoAdmin\Bundle\Content\Event\ContentMatchEvent;
use AutoAdmin\Bundle\Content\Repository\DefaultTranslatableContentRepository;
use Doctrine\Persistence\ManagerRegistry;
use Exception;
use Psr\Log\LoggerInterface;
use ReflectionClass;

class ContentMatcherListener
{
    protected ConfigManager $config;
    private ManagerRegistry $doctrine;
    private LoggerInterface $logger;

    public function __construct(ConfigManager $config, ManagerRegistry $doctrine, LoggerInterface $logger)
    {
        $this->config = $config;
        $this->doctrine = $doctrine;
        $this->logger = $logger;
    }

    public function onContentMatch(ContentMatchEvent $event): void
    {
        $contentContext = $event->getContentContext();
        $request = $event->getRequest();

        $route = $request->get('_route');
        if (!$this->isSupportedRoute($route)) {
            return;
        }

        $contentClass = $this->config->getContentClassForRoute($route);
        $slugAndAction = $this->config->getRouteSlugAndAction($route);
        if ($contentClass === null) {
            $warn = 'Could not match $mainContent on url "'.$request->getUri().'" and route "'.$route.'"';
            $this->logger->warning($warn);
        } elseif ($slugAndAction) {
            $contentRefl = new ReflectionClass($contentClass);
            if ($contentRefl->isSubclassOf(Content::class)) {
                /*
                 * Auto content matching, here we try to find matching content with available info
                 * before arriving in controllers
                 */
                try {
                    /** @var DefaultTranslatableContentRepository $repository */
                    $repository = $this->doctrine->getRepository($contentClass);
                    if ($route === 'page_index') {
                        $mainContent = $repository->contentFindOneBy(['_.homepage' => true]);
                        if (empty($mainContent)) {
                            throw new Exception('You must define one page as "homepage".');
                        }
                        $contentContext->setMainContent($mainContent);
                    } else {
                        $findParameters = [];
                        $routeConfig = $this->config->getRoute($route);
                        if ($routeConfig && !empty($routeConfig['autofill_parameters'])) {
                            foreach ($routeConfig['autofill_parameters'] as $param => $path) {
                                $tableDepthCounter = 0;

                                // we shouldn't have this format here but replace to be safe
                                $path = str_replace('currentTranslation.', '_tr.', $path);

                                if (str_starts_with($path, '_.')) {
                                    $prefix = '_.';
                                } elseif (str_starts_with($path, '_tr.')) {
                                    $prefix = '_tr.';
                                } else {
                                    $prefix = '_.';// default to translatable alias
                                    $tableDepthCounter++;
                                }

                                $tableDepthCounter += substr_count($path, '_tr.');
                                $tableDepthCounter += substr_count($path, '_.');

                                if ($tableDepthCounter > 1) {
                                    // we don't want to handle only simple contexts, so only alias.field format,
                                    // as deeper paths will lead to missing doctrine joints
                                    // until this joints problems are handled, we reject complex cases
                                    // (leaving content matching to the controllers)
                                    break;
                                }

                                // here, prefix is table alias, param is first-level field name
                                // (param is also used to get request route value and construct doctrine criteria)
                                $findParameters[$prefix.$param] = $request->get($param);
                            }

                            if (!empty($findParameters)) {
                                $mainContent = $repository->contentFindOneBy($findParameters);
                                if ($mainContent) {
                                    $contentContext->setMainContent($mainContent);
                                }
                            }
                        }

                    }
                } catch (Exception $e) {
                    error_log($e->getMessage());
                }
            }
        }
    }

    protected function isSupportedRoute($route): bool
    {
        if ($route === 'page_index') {
            return true;
        }

        $hasRoute = $this->config->hasRoute($route);
        if ($hasRoute
            && ($routeConfig = $this->config->getRoute($route))
            && !empty($routeConfig['autofill_parameters'])
        ) {
            return true;
        }

        return false;
    }
}
