<?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  2019 Lifestyle Webconsulting GmbH
 * @link       https://www.life-style.de
 */

namespace SimpleSAML\Module\lifestyle\Store;

use Exception;
use SimpleSAML\Logger;
use SimpleSAML\Session;

/**
 * Class SQL
 *
 * @copyright  2019 Lifestyle Webconsulting GmbH
 * @link       https://www.life-style.de
 * @package SimpleSAML\Module\lifestyle\Store
 */
class SQL extends \SimpleSAML\Store\SQL
{
    /**
     * SQL constructor.
     * @throws Exception
     */
    public function __construct()
    {
        parent::__construct();
        $this->modifyKVTable();
    }

    /**
     * Modify key-value table.
     */
    private function modifyKVTable()
    {
        if ($this->getTableVersion('kvstore_extra') === 1) {
            /* Table initialized. */
            return;
        }

        $query = 'ALTER TABLE ' . $this->prefix . '_kvstore ADD COLUMN `_extra` VARCHAR(128) DEFAULT NULL';
        $this->pdo->exec($query);

        $query = 'CREATE INDEX ' . $this->prefix . '_kvstore_uid ON ' . $this->prefix . '_kvstore (_extra,_expire)';
        $this->pdo->exec($query);

        $this->setTableVersion('kvstore_extra', 1);
    }

    /**
     * Save a value to the datastore.
     *
     * @param string $type The datatype.
     * @param string $key The key.
     * @param mixed $value The value.
     * @param int|NULL $expire The expiration time (unix timestamp), or NULL if it never expires.
     */
    public function set($type, $key, $value, $expire = null)
    {
        assert('is_string($type)');
        assert('is_string($key)');
        assert('is_null($expire) || (is_int($expire) && $expire > 2592000)');

        if (rand(0, 1000) < 10) {
            $this->cleanKVStore();
        }

        if (strlen($key) > 50) {
            $key = sha1($key);
        }

        if ($expire !== null) {
            $expire = gmdate('Y-m-d H:i:s', $expire);
        }

        $extra = $this->_extractUserIdentifier($value);
        $value = serialize($value);
        $value = rawurlencode($value);

        $data = array(
            '_type' => $type,
            '_key' => $key,
            '_value' => $value,
            '_expire' => $expire,
            '_extra' => $extra,
        );

        $this->insertOrUpdate($this->prefix . '_kvstore', array('_type', '_key'), $data);
    }

    /**
     * Clean the key-value table of expired entries.
     */
    private function cleanKVStore()
    {

        Logger::debug('store.sql: Cleaning key-value store.');

        $query = 'DELETE FROM ' . $this->prefix . '_kvstore WHERE _expire < :now';
        $params = array('now' => gmdate('Y-m-d H:i:s'));

        $query = $this->pdo->prepare($query);
        $query->execute($params);
    }

    /**
     * Extract user identifier from session
     *
     * @param Session $session
     * @return string|null
     */
    protected function _extractUserIdentifier($session)
    {
        if (!($session instanceof Session)) {
            return null;
        }
        $authorities = $session->getAuthorities();
        foreach ($authorities as $authority) {
            $attributes = $session->getAuthData($authority, 'Attributes');
            if (isset($attributes['useridentifier']) && is_array($attributes['useridentifier'])) {
                $userIdentifier = reset($attributes['useridentifier']);
                if (0 < strlen($userIdentifier)) {
                    return $userIdentifier;
                }
            }
        }
        return null;
    }
}
