<?php
/**
 * Saml cookie handler
 *
 * LICENSE: This Software is the property of Lifestyle Webconsulting GmbH (Aschaffenburg, Germany)
 * and is protected by copyright law - it is NOT Freeware.
 *
 * Any unauthorized use of this software without a valid license
 * is a violation of the license agreement and will be prosecuted by
 * civil and criminal law.
 *
 * @copyright  2015 Lifestyle Webconsulting GmbH
 * @link       http://www.life-style.de
 */

namespace AppBundle\EventListener\Cookie;

use Doctrine\ORM\EntityManager;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use LifeStyle\Tools\SamlBundle\Event\FilterCookieFileEvent;
use LifeStyle\Tools\SamlBundle\Event\CookieFile as CookieFileEvent;
use AppBundle\Entity\CookieCache;
use AppBundle\Api\Manager as ApiManager;

/**
 * Saml cookie handler
 */
class DatabaseHandler implements EventSubscriberInterface
{

    /**
     * Api Manager
     *
     * @var ApiManager
     */
    private $apiM;

    /**
     * Doctrine entity manager
     *
     * @var EntityManager
     */
    protected $entityManager;

    /**
     * Database cache entity
     *
     * @var CookieCache
     */
    protected $cache;

    /**
     * Cookie filename
     *
     * @var string
     */
    protected $filename;

    /**
     * Content of current cookie file
     *
     * @var string
     */
    protected $currentCookie;

    /**
     * Constructor
     *
     * @param ApiManager $apiManager
     */
    public function __construct(ApiManager $apiManager)
    {
        $this->apiM = $apiManager;
        $this->entityManager = $apiManager->database();
    }

    /**
     *
     * @param FilterCookieFileEvent $filterEvent
     */
    public function onCookieLoad(FilterCookieFileEvent $filterEvent)
    {
        $path = $filterEvent->getCookieFile();
        if (null !== $this->filename) {
            $path = dirname($path) . '/' . trim($this->filename, '/');
        }
        if (!file_exists(dirname($path))) {
            mkdir(dirname($path), 0700, true);
        }
        $filterEvent->setCookieFile($path);

        $this->cache = $this->entityManager->find('AppBundle\Entity\CookieCache', basename($path));
        file_put_contents($path, null !== $this->cache ? $this->cache->getContent() : '');
    }

    /**
     *
     * @param FilterCookieFileEvent $filterEvent
     */
    public function onCookieSave(FilterCookieFileEvent $filterEvent)
    {
        if (null === $this->cache) {
            $this->cache = new CookieCache($this->filename);
        }
        $this->cache->setContent(file_get_contents($filterEvent->getCookieFile()));
        $this->updateExpire($this->cache);
    }

    /**
     *
     * @param FilterCookieFileEvent $filterEvent
     */
    public function onCookieRemove(FilterCookieFileEvent $filterEvent)
    {
        $cache = $this->entityManager->find('AppBundle\Entity\CookieCache', $this->filename);
        if (null !== $cache) {
            $this->entityManager->remove($cache);
        }
    }

    /**
     *
     * @param FilterCookieFileEvent $filterEvent
     */
    public function onCookieCleanup(FilterCookieFileEvent $filterEvent)
    {
        $this->updateExpire($this->cache);
        $this->rotate();
        if (file_exists($filename = $filterEvent->getCookieFile())) {
            unlink($filename);
        }
    }

    /**
     * Set cookie filename
     *
     * @param string $filename
     */
    public function setFilename($filename)
    {
        $this->filename = (string) $filename;
    }

    /**
     * Copy cookie in database
     *
     * @param string $filename
     * @return boolean True on success
     */
    public function copyCookie($filename)
    {
        if (null === $this->cache) {
            return false;
        }
        $cache = ($cache = $this->entityManager->find('AppBundle\Entity\CookieCache', $filename)) ? $cache : new CookieCache($filename);
        $cache->setContent($this->cache->getContent());
        $this->updateExpire($cache);
        return true;
    }

    /**
     * Update cookie expire in database
     */
    private function updateExpire(CookieCache $cache = null)
    {
        if (null === $cache) {
            return;
        }
        $cache->updateExpire();
        $this->entityManager->persist($cache);
        $this->entityManager->flush($cache);
    }

    /**
     * Clean up table
     */
    private function rotate()
    {
        if (rand(0, 100) > 25) {
            return;
        }
        $date = new \DateTime();
        $queryBuilder = $this->entityManager->createQueryBuilder();
        $queryBuilder->delete('AppBundle\Entity\CookieCache', 'c')
                ->andWhere($queryBuilder->expr()->lt('c.expire', ':currentDate'));
        $this->entityManager->createQuery($queryBuilder)->setParameter('currentDate', $date->format('Y-m-d H:i:s'))->execute();
    }

    /**
     * Get list of subscribed events
     *
     * @return array
     */
    public static function getSubscribedEvents()
    {
        return array(
            CookieFileEvent::LOAD => 'onCookieLoad',
            CookieFileEvent::SAVE => 'onCookieSave',
            CookieFileEvent::REMOVE => 'onCookieRemove',
            CookieFileEvent::SHUTDOWN => 'onCookieCleanup',
        );
    }
}
