<?php

namespace AutoAdmin\Bundle\Content\Routing;

use AutoAdmin\Bundle\Content\Entity\Page;
use AutoAdmin\Bundle\Content\Model\ContentInterface;
use AutoAdmin\Bundle\Content\Routing\Exception\RouteNotFoundException;
use Exception;
use ReflectionClass;

class DefaultContentAutoRouter extends AbstractContentAutoRouter
{
    public function supports($subject): bool
    {
        return true; // default behavior, we try to build url for any content
    }

    public function buildUrl(
        ContentRouter $contentRouter,
        object $subject,
        ?string $route,
        array $parameters = [],
        array $options = []
    ): string {
        if ($route === null) {
            throw new RouteNotFoundException(
                'Could not generate route for object of class "'.get_class($subject).'", route is null.'
            );
        }

        // check if exists "locale" in $options
        if (!isset($options['locale'])) {
            $options['locale'] = $this->localeResolver->resolveLocale();
        }


        $routeParts = explode('_', $route, 2);
        $contentSlug = $routeParts[0] ?? null;
        $action = $routeParts[1] ?? null;
        if (empty($contentSlug) || empty($action)) {
            throw new Exception('Could not identify content slug from action slug in string: "'.$route.'"');
        }

        list($contentSlug, $action) = $this->filterSpecialRoutes($subject, $contentSlug, $action);

        $builtParams = $this->buildRouteParams($subject, $contentSlug, $action, $options, $parameters);
        $parameters = array_merge($builtParams, $parameters);

        return $this->router->generate($route, $parameters);
    }

    protected function filterSpecialRoutes($content, string $contentSlug, string $routeAction): array
    {
        // specific case: if we are handling the homepage, use "index" action instead of "view" action

        if (is_object($content)) {
            $contentClassRefl = new ReflectionClass($content);
            /** @var Page $content */
            if ($contentSlug === 'page' && $contentClassRefl->isSubclassOf(Page::class) && $content->isHomepage()) {
                $routeAction = 'index';
            }
        }

        return [$contentSlug, $routeAction];
    }

    public function buildRouteParams(
        $content,
        $contentSlug,
        $action = 'view',
        array $options = [],
        array $parameters = []
    ): array {
        list($contentSlug, $action) = $this->filterSpecialRoutes($content, $contentSlug, $action);
        $routeName = $this->getRouteName($content, $contentSlug, $action, $options);

        if (!isset($parameters['_locale'])) {
            $parameters = $this->addImplicitParameters($routeName, $parameters);
        }

        $contentClass = $this->config->getContentClassForRoute($routeName);

        $refl = new ReflectionClass($contentClass);
        if ($content !== null && $refl->isSubclassOf(ContentInterface::class)) {
            $parameters = $this->addAutoFilledParameters($routeName, $parameters, $content);
        }

        return $parameters;
    }

    public function getRouteName($content, string $contentSlug, string $action = 'view', array $options = []): ?string
    {
        list($contentSlug, $action) = $this->filterSpecialRoutes($content, $contentSlug, $action);

        $routeName = $contentSlug.'_'.$action;
        $routes = $this->router->getRouteCollection();
        $route = $routes->get($routeName);
        if ($route === null) {
            $locale = $this->localeResolver->resolveLocale($options['locale'] ?? null);
            $routeName .= '.'.$locale;
            $route = $routes->get($routeName);
        }

        if ($route === null) {
            return null;
        }

        return $routeName;
    }

    protected function addImplicitParameters(string $routeName, array $parameters = []): array
    {
        // auto-add locale if parameter is defined in route
        $route = $this->router->getRouteCollection()->get($routeName);
        if ($route) {
            $routePath = $route->getPath();
            if (stripos($routePath, '{_locale}') !== false) {
                $parameters['_locale'] = $this->localeResolver->resolveLocale($parameters['_locale'] ?? null);
            }
        }

        return $parameters;
    }
}
