<?php
/**
 * @author Ulrich Hecht <ulrich.hecht@hecht-software.de>
 * @date 12/02/2016
 */

namespace AppBundle\Listener;


use AppBundle\AppBundle;
use AppBundle\Controller\DefaultController;
use AppBundle\Entity\Page;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\PersistentCollection;
use Symfony\Bundle\FrameworkBundle\Routing\Router;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

class KernelControllerListener
{
    private $em;
    private $controllerResolver;

    public function __construct(EntityManager $entityManager, ControllerResolverInterface $controllerResolver)
    {
        $this->em = $entityManager;
        $this->controllerResolver = $controllerResolver;
    }

    public function onKernelController(FilterControllerEvent $event)
    {
        $request = $event->getRequest();
        if ($request->get('_controller') == 'AppBundle\Controller\DefaultController::defaultAction')
        {
            $repo = $this->em->getRepository('AppBundle:Page');
            $path = preg_replace('/^\/?(.*?)\/?$/', '$1', $request->getPathInfo());
            /** @var Page $node */
            $node = null;

            // Try to find by url path. It's possible that the path consists of two parts:
            // - the beginning part represents a node
            // - the ending part represents a handler
            // e.g. /path/to/travel/program/buchen ("buchen" is the handler part)
            $pathArray = explode('/', $path);
            $restOfPath = '';
            $curPath = $path;
            while (!empty($pathArray))
            {
                $node = $repo->findOneBy(['realUrlPath' => '/'. $curPath]);
                if ($node)
                {
                    break;
                }
                $restOfPath = '/'. array_pop($pathArray) . $restOfPath;
                $curPath = implode('/', $pathArray);
            }

            if (!$node)
            {
                // Now try to find a page by tracing a page node path using the page nodes' slugs

                $pathArray = explode('/', $path);
                $i = 0;
                while (!empty($pathArray))
                {
                    $restOfPath = '/'. implode('/', $pathArray);
                    $slug = array_shift($pathArray);

                    $qb = $repo->createQueryBuilder('p');
                    $qb->where($qb->expr()->eq('p.lvl', $i));
                    $qb->andWhere($qb->expr()->eq('p.slug', ':slug'));
                    $qb->setParameter('slug', $slug);

                    if ($node != null)
                    {
                        $qb->andWhere($qb->expr()->between('p.lft', $node->getLft(), $node->getRgt()));
                    }
                    $qb->setMaxResults(1);
                    $childNode = $qb->getQuery()->getOneOrNullResult();
                    if (!$childNode)
                    {
                        break;
                    }
                    if ($node)
                    {
                        // Avoid database calls to parent later
                        $childNode->setParent($node);
                    }
                    $node = $childNode;
                    ++$i;
                }
                if ($node && $node->getRealUrlPath() && $node->getRealUrlPath() != '/'. $path)
                {
                    // If there realUrlPath is set and the slug path differs from realUrlPath, then the slug path is
                    // not a valid URL. Otherwise, there would be two different URLs representing the same page.
                    $event->setController(function() {
                        throw new NotFoundHttpException('Invalid URL');
                    });
                    return;
                }
            }
            if (!$node)
            {
                // Search for a redirect entry
                $redirect = $this->em->getRepository('AppBundle:Redirect')->findOneBy(['sourceUrlPath' => '/'. $path]);
                if ($redirect)
                {
                    $redirectUrl = $redirect->getPage()->getUrlPath();
                    $event->setController(function() use ($redirectUrl) {
                        return new RedirectResponse($redirectUrl, 301);
                    });
                    return;
                }
            }
            if ($node)
            {

                if ($node->getStatus() == 0)
                {
                    throw new NotFoundHttpException('Inactive page');
                }

                $request->attributes->set('page', $node);

                if ($node->getTravelProgram() != null && (
                    $restOfPath == '/buchen' || $restOfPath == '/berechne-gesamtpreis'))
                {
                    // Special case: Booking actions
                    $request->attributes->set('travelProgramPage', $node);
                    $request->attributes->set('action', $restOfPath);
                    $request->attributes->set('_controller', 'AppBundle:Booking:index');
                }
                elseif($restOfPath && $node->getTravelProgram() != null && (
                    $restOfPath == '/pdf'))
                {
                    $request->attributes->set('_controller', 'AppBundle:Cms:pdf');
                }
                elseif ($node->getTravelProgram() != null)
                {
                    if ($node->getTravelProgram()->getStatus() == 0)
                    {
                        throw new NotFoundHttpException('Inactive travel program');
                    }
                    $request->attributes->set('_controller', 'AppBundle:Cms:travelProgram');
                }
                elseif ($node->getFewoLodging() != null &&
                    ($restOfPath == '/buchen' || $restOfPath == '/berechne-gesamtpreis'))
                {
                    $request->attributes->set('fewoTravelProgramPage', $node);
                    $request->attributes->set('action', $restOfPath);
                    $request->attributes->set('_controller', 'AppBundle:FewoBooking:index');
                }
                elseif ($node->getFewoLodging() != null)
                {
                    $request->attributes->set('fewoLodgingPage', $node);
                    $request->attributes->set('action', $restOfPath);
                    $request->attributes->set('_controller', 'AppBundle:Cms:fewoLodging');
                }
                else
                {
                    $handler = $node->getTemplate() ? ucfirst($node->getTemplate()) : 'Default';
                    $request->attributes->set('_controller', 'AppBundle:Cms:'. $handler);
                    if ($node->getTemplate())
                    {
                        try
                        {
                            $controller = $this->controllerResolver->getController($request);
                        }
                        catch (\LogicException $e)
                        {
                            // If there is no controller action, call the default action and pass the template name
                            $request->attributes->set('_controller', 'AppBundle:Cms:Default');
                            $request->attributes->set('template', $node->getTemplate());
                        }
                    }
                }
            }
            else
            {
                return;
            }
            $event->setController($controller ?? $this->controllerResolver->getController($request));
        }
    }
}