<?php

/**
 * Lifestyle Webconsulting GmbH
 *
 * LICENSE: This Software is the property of Lifestyle Webconsulting GmbH (Aschaffenburg, Germany)
 * and is private 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  2018 Lifestyle Webconsulting GmbH
 * @link       http://www.life-style.de
 */

namespace LifeStyle\Tools\CachingBundle\Api\Data\Common;

use Symfony\Component\HttpFoundation\Request;
use LifeStyle\Tools\CachingBundle\Annotations\LsCache;
use LifeStyle\Tools\CachingBundle\Api\Manager as ApiManager;

/**
 * Class Functions
 * @package LifeStyle\Tools\CachingBundle\Api\Data\Common
 */
class Functions
{
    /**
     * @var ApiManager
     */
    private $apiM;

    /**
     * Factory constructor.
     * @param ApiManager $apiM
     */
    public function __construct(ApiManager $apiM)
    {
        $this->apiM = $apiM;
    }

    /**
     * Generate cache key hash based on request and configuration data
     *
     * @param Request $request
     * @param LsCache $configuration
     * @return string
     */
    public function generateCacheKey(Request $request, LsCache $configuration)
    {
        $format = $request->attributes->get('_format', 'json');

        // build header part for the key-hash, if it's requested to include headers for the cache key
        $headers = '';
        if ($configuration->isKeyHeader()) {
            $headers .= $request->headers->get('accept');
            $headers .= $request->headers->get('username', 'username');
            $headers .= $request->headers->get('servicetoken', 'servicetoken');
            $headers .= $request->headers->get('useridentifier', 'useridentifier');
        }

        // get all values in the query for using them for the key-hash....
        $getParamsArray = $request->query->all();
        // ....but leave out our control values so they don't cause separate cache entries for themselves
        unset($getParamsArray['nocache']);
        unset($getParamsArray['rebuildcache']);
        // sort the values to always get the same key-hash independent of the order in the actual request
        ksort($getParamsArray);

        $bodyHash = '';
        // if this is a POST request we need to include the post body in the key has as well
        if ($request->getMethod() == 'POST') {
            $bodyHash = md5($this->stripWhitespace($request->getContent(), $format));
        }

        // time to get that key-hash
        return $request->attributes->get('_route') . "-" . md5($headers . http_build_query($getParamsArray) . $format . $bodyHash);
    }

    /**
     * Compresses / strips any whitespace from the input string without breaking XML or JSON structures or content
     *
     * @param string $string
     * @param string $format
     * @return string
     */
    public function stripWhitespace($string, $format = '')
    {
        $regex = '/.*/';
        $contentCaptureGroup = 0;
        if ('xml' == $format) {
            $regex = '/<\\w+?>(.*?)<\\/\\w+?>/';
            $contentCaptureGroup = 1;
        } elseif ('json' == $format) {
            $regex = '/"(.*?\\s*?.*?)"/';
            $contentCaptureGroup = 1;
        }

        // first save everything we don't want to alter, i.e. data and content
        $matches = [];
        $savedContent = [];
        preg_match_all($regex, $string, $matches);
        for ($i = 0; $i < count($matches[0]); $i++) {
            $key = '{{' . (string)$i . '}}';
            $replacementString = str_replace($matches[$contentCaptureGroup][$i], $key, $matches[0][$i]);
            $savedContent[$key] = $matches[$contentCaptureGroup][$i];
            $string = str_replace($matches[0][$i], $replacementString, $string);
        }

        // now throw out all that whitespace
        $string = str_replace("\n", '', $string);
        $string = str_replace("\r", '', $string);
        $string = str_replace("\t", '', $string);
        $string = str_replace(' ', '', $string);

        // put back the saved content
        foreach ($savedContent as $key => $value) {
            $string = str_replace($key, $value, $string);
        }

        return $string;
    }
}
