<?php
namespace Backend\BaseBundle\EventListener;

use Backend\BaseBundle\Service\RequestSiteFinder;
use Backend\BaseBundle\SiteConfig\SiteConfigBuilder;
use JMS\DiExtraBundle\Annotation as DI;
use JMS\DiExtraBundle\Annotation\Inject;
use JMS\DiExtraBundle\Annotation\InjectParams;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;

/**
 * @DI\Service
 */
class APICORSResponseListener
{
    /** @var  SiteConfigBuilder */
    protected $siteConfigBuilder;

    /** @var  RequestSiteFinder */
    protected $requestSiteFinder;

    /**
     * @InjectParams({
     *    "siteConfigBuilder" = @Inject("backend_base.site_config_builder"),
     * })
     */
    public function injectSiteConfigBuilder($siteConfigBuilder)
    {
        $this->siteConfigBuilder = $siteConfigBuilder;
    }

    /**
     * @InjectParams()
     */
    public function injectRequestSiteFinder(RequestSiteFinder $requestSiteFinder)
    {
        $this->requestSiteFinder = $requestSiteFinder;
    }

    /**
     * @DI\Observe("kernel.request")
     */
    public function onRequest(GetResponseEvent $event)
    {
        if(!$event->isMasterRequest()) {
            return;
        }

        $request = $event->getRequest();

        if( ($origin = $request->headers->get('origin')) === null ||
            ($slug = $request->attributes->get('slug')) === null ||
            ($request->isMethod(Request::METHOD_POST) && $request->headers->get('Content-Type') == 'application/x-www-form-urlencoded')
        ){
            return;
        }

        if($this->isSameOrigin($request)){
            $this->setCors($request, $origin);
            return;
        }

        if(!$this->isValidOrigin($origin, $slug)){
            $event->setResponse(new Response('CORS Access Deny', Response::HTTP_FORBIDDEN));
            return;
        }

        $this->setCors($request, $origin);
    }

    protected function setCors(Request $request, $origin)
    {
        $request->attributes->set('_cors', array(
            'Access-Control-Allow-Origin' => $origin,
            'Access-Control-Allow-Methods' => 'PUT, GET, POST, DELETE, OPTIONS',
            'Access-Control-Allow-Headers' => 'X-Requested-With, Content-Type, Accept, Authorization',
            'Access-Control-Max-Age' => 86400,  // 1 day
        ));
    }

    protected function isSameOrigin(Request $request)
    {
        return $request->getSchemeAndHttpHost() === $request->headers->get('origin');
    }

    /**
     * @DI\Observe("kernel.response")
     */
    public function onResponse(FilterResponseEvent $event)
    {
        if(!$event->isMasterRequest()) {
            return;
        }

        $request = $event->getRequest();
        $response = $event->getResponse();

        if($cors = $request->attributes->get('_cors')) {
            $this->setResponseCORS($response, $cors);
        }
    }

    protected function setResponseCORS(Response $response, $cors)
    {
        foreach($cors as $key => $val){
            $response->headers->set($key, $val);
        }
    }

    protected function isValidOrigin($origin, $slug)
    {
        $domains = $this->findAcceptDomain($slug);
        if(!preg_match('/^https?:\/\/(.*)/i', $origin, $match)){
            return false;
        }
        $host = strtolower($match[1]);
        return $domains && in_array($host, $domains);
    }

    protected function findAcceptDomain($slug)
    {
        $site = $this->requestSiteFinder->getSite($slug);

        if(!$site){
            return null;
        }

        $config = $this->siteConfigBuilder->build($site->getId())->get('system');

        if(!isset($config['domains'])){
            return null;
        }

        return explode(',', strtolower(trim($config['domains'])));
    }
}