<?php

namespace Velis\Throttling;

use Velis\App;
use Velis\Throttling\LeakyBucket\SessionStorage;
use Velis\Throttling\LeakyBucket\CacheStorage;
use Velis\Throttling\LeakyBucket\DatabaseStorage;

/**
* LeakyBucket algorithm class for request throttling
*/
class LeakyBucket
{
    /**
    * Ajax request cost
    */
    const AJAX_REQUEST_COST = 1;

    /**
    * Request cost
    */
    const REQUEST_COST = 2;
    /**
    * Max requests
    */
    const MAX_CONTENT = 10000;

    /**
    * Time interval
    */
    const TIME = 3600;

    private $_data;
    private $_interval;
    private $_maxContent;
    public $interval;

    /**
    * Class constructor - choose data class
    */
    public function __construct($public)
    {
        $request = App::getService('request');

        if (!App::$user->isLogged()) {
            if ($public && !($request->isAuthorization() || $request->isLogin())) {
                $this->_data = new CacheStorage('public');
            } else {
                $this->_data = new DatabaseStorage();
            }
        } else if (App::$user->isLogged() && $request->isApi()) {
            $this->_data = new CacheStorage();
        } else if (App::$user->isLogged()) {
            $this->_data = new SessionStorage();
        }
        $class = get_class($this->_data);
        $this->_interval = $class::getInterval();
        $this->_maxContent = $class::getMaxContent() ?: self::MAX_CONTENT;
        $this->_calculate();
    }


    /**
    * Main algorithm method
    */
    protected function _calculate()
    {
        $request = App::getService('request');

        if ($this->_data instanceof DatabaseStorage && !($request->isAuthorization() || $request->isLogin())) {
            //don't calculate if not login action
            return;
        }

        $this->interval = 0;

        $tmp = $this->_data->counter - (($this->dateDiff(date('Y-m-d H:i:s'), $this->_data->lct)) / self::TIME);
        if ($tmp < 0) {
            $tmp = 0;
        }

        if ($tmp > $this->_maxContent) {
            $this->interval = $this->_interval;
            sleep($this->_interval);
        } else {
            $this->_data->counter = $tmp + $this->_getRequestCost();
            $this->_data->lct = date('Y-m-d H:i:s');
        }
    }


    /**
    * Calculate diff between dates in hours
    */
    public function dateDiff($date1, $date2)
    {
        $datetime1 = date_create($date1);
        $datetime2 = date_create($date2);

        $interval = date_diff($datetime1, $datetime2);
        return $interval->format('%h');
    }


    /**
    * Returns request cost
    */
    protected function _getRequestCost()
    {
        $request = App::getService('request');

        if ($request && $request->isAjax(true)) {
            return self::AJAX_REQUEST_COST;
        }
        return self::REQUEST_COST;
    }
}
