<?php
/**
 * core.framework
 *
 * @category  Core
 * @package   Core_Application
 * @copyright Copyright (c) 2011. Burza d.o.o. (http://web.burza.hr/en/)
 * @license   proprietary
 */

/**
 * @category  Core
 * @package   Core_Application
 * @copyright Copyright (c) 2011. Burza d.o.o. (http://web.burza.hr/en/)
 * @license   proprietary
 */
class Core_Application_Plugin_EventHandler_RequestValidator extends Core_Application_Plugin_EventHandler_Abstract
{
    /**
     * @var array
     */
    protected $_subscriptions = array(
        array(
            'event'    => Core_Dispatcher::EVENT_PREDISPATCH,
            'method'   => 'payloadValidatorHandler',
            'priority' => -100,
        ),
        array(
            'event'    => Core_Dispatcher::EVENT_PREDISPATCH,
            'method'   => 'methodValidatorHandler',
            'priority' => -900,
        ),
    );

    /**
     * @var Core_Request
     */
    protected $_request;

    /**
     * @var Core_Response
     */
    protected $_response;

    /**
     * @var array
     */
    protected $_supportedMethods = array(
        'POST',
        'GET',
        'HEAD',
        'PUT',
        'PATCH',
        'DELETE',
        'OPTIONS'
    );

    /**
     * @var array
     */
    protected $_supportedHeaders = array(
        'Authorization',
        'X-Requested-With',
    );

    /**
     * @param Core_Request $request
     *
     * @return \Core_Application_Plugin_EventHandler_RequestValidator
     */
    public function setRequest(Core_Request $request)
    {
        $this->_request = $request;
        return $this;
    }

    /**
     * @return Core_Request
     */
    public function getRequest()
    {
        if (null === $this->_request) {
            $this->setRequest(Core_Application::get('Request'));
        }
        return $this->_request;
    }

    /**
     * @param Core_Response $response
     *
     * @return \Core_Application_Plugin_EventHandler_RequestValidator
     */
    public function setResponse(Core_Response $response)
    {
        $this->_response = $response;
        return $this;
    }

    /**
     * @return Core_Response
     */
    public function getResponse()
    {
        if (null === $this->_response) {
            $this->setResponse(Core_Application::get('Response'));
        }
        return $this->_response;
    }

    /**
     * @param array $supportedMethods
     *
     * @return \Core_Application_Plugin_EventHandler_RequestValidator
     */
    public function setSupportedMethods(array $supportedMethods)
    {
        // we always support OPTIONS
        if (!in_array(Core_Request::METHOD_OPTIONS, $supportedMethods)) {
            $supportedMethods[] = Core_Request::METHOD_OPTIONS;
        }
        $this->_supportedMethods = $supportedMethods;
        return $this;
    }

    /**
     * @return array
     */
    public function getSupportedMethods()
    {
        return $this->_supportedMethods;
    }

    /**
     * @param array $supportedHeaders
     *
     * @return \Core_Application_Plugin_EventHandler_RequestValidator
     */
    public function setSupportedHeaders(array $supportedHeaders)
    {
        $this->_supportedHeaders = $supportedHeaders;
        return $this;
    }

    /**
     * @return array
     */
    public function getSupportedHeaders()
    {
        return $this->_supportedHeaders;
    }

    /**
     * @throws Core_Application_InvalidInputException
     * @return void
     */
    public function payloadValidatorHandler()
    {
        $request          = $this->getRequest();
        if (!$request->isValid()) {
            $message = sprintf('Invalid request payload - %s', $request->getError());
            throw new Core_Application_InvalidInputException($message);
        }
    }

    /**
     * @param Core_Event_Interface $event
     *
     * @return void
     */
    public function methodValidatorHandler(Core_Event_Interface $event)
    {
        $request          = $this->getRequest();

        // TODO: this is context-specific, need to pull it out of the controller (or something)
        $supportedMethods = $this->getSupportedMethods();

        if (!in_array($request->getMethod(), $supportedMethods)) {
            // invalid method
            $event->setIsProcessed(true);
            $response = $this->getResponse();
            $response
                ->setCode(Core_Response::STATUS_METHOD_NOT_ALLOWED)
                ->setHeader('Allow', implode(', ', $supportedMethods));
            return $response;
        } else if ($request->isMethod(Core_Request::METHOD_OPTIONS)) {
            // CORS preflight
            $event->setIsProcessed(true);
            $supportedHeaders = $this->getSupportedHeaders();
            $response         = $this->getResponse();
            $response
                ->setHeader('Allow',                        implode(', ', $supportedMethods))
                ->setHeader('Access-Control-Allow-Methods', implode(', ', $supportedMethods))
                ->setHeader('Access-Control-Allow-Headers', implode(', ', $supportedHeaders))
                ->setHeader('Access-Control-Max-Age',       360);
            return $response;
        }
    }
}
