<?php

namespace WtbFirewall\RateLimit;

// Prevent direct access.
if (!defined('ABSPATH')) {
    exit;
}

/**
 * Hovedklassen for å implementere rate limiting og 404 monitor.
 */
class RateLimit {

    public function __construct() {
        $this->Start();
    }

    public function Start() {

        add_action('shutdown', [$this, 'rateLimit']);
    }

    /**
     * Kalles via  shutdown  action.
     * Logfører requests og 404 feil, samt sjekker om rate limiting er nødvendig.
     */
    public function rateLimit() {

        //Ingen sjekk for brukere som er innlogget.
        if (is_user_logged_in()) {
            return;
        }

        require_once WTB_FIREWALL_PATH . 'RateLimit/Includes.php';
        \WtbFirewall\Config\FirewallConfig::loadOptions();

        $rateLimitLog = new \WtbFirewall\RateLimit\RateLimitLog();

        if (\WtbFirewall\Config\FirewallConfig::$rateLimit['enabled'] == true) {
            $rateLimitLog->logRequest();

            //Sjekk om vi har nådd grensen for antall requests.
            if ($rateLimitLog->getRequestCount() >= \WtbFirewall\Config\FirewallConfig::$rateLimit['requestsPerMinute']) {
                if ($this->blockConnection('Exceeded number of allowed requests per minute.', \WtbFirewall\Config\FirewallConfig::$rateLimit['blockMinutes'])) {
                    //IP ble blokkert, logfør dette.
                    $note = 'Exceeded number of allowed requests per minute. Blocked for ' . \WtbFirewall\Config\FirewallConfig::$rateLimit['blockMinutes'] . ' minutes.';
                    $this->logBlock(\WtbFirewall\Firewall\EventType::RATELIMIT_REQUESTS, $note);
                }
            }
        }

        if (\WtbFirewall\Config\FirewallConfig::$rateLimit['errors404Enabled'] == true && is_404()) {
            $rateLimitLog->log404();

            //Sjekk om vi har nådd grensen for antall 404 feil.
            if ($rateLimitLog->get404Count() >= \WtbFirewall\Config\FirewallConfig::$rateLimit['errors404PerMinute']) {
                if ($this->blockConnection('Exceeded number of allowed 404 errors per minute.', \WtbFirewall\Config\FirewallConfig::$rateLimit['errors404BlockMinutes'])) {
                    //IP ble blokkert, logfør dette.
                    $note = 'Exceeded number of allowed 404 errors per minute. Blocked for ' . \WtbFirewall\Config\FirewallConfig::$rateLimit['errors404BlockMinutes'] . ' minutes.';
                    $this->logBlock(\WtbFirewall\Firewall\EventType::RATELIMIT_404, $note);
                }
            }
        }
    }

    /**
     * Blokkerer nåværende IP midlertidig.
     * Dersom IP er allerede blokkert gjøres ingenting.
     * 
     * @param string $note Beskrivelse av firewall regelen.
     * @param int $time Hvor lenge blokkeringen skal være i minutter.
     * @return bool TRUE dersom ip ble blokkert, false hvis ikke.
     */
    private function blockConnection(string $note, int $time): bool {
        $firewall = new \WtbFirewall\Firewall\Firewall();

        //Sjekk at vi ikke allerede har en regel for denne IP addressen.              
        if ($firewall->autoRulesExists(\WtbFirewall\Helpers\IpHelper::getVisitorIP())) {
            return false;
        }

        $dt = new \DateTime('now', new \DateTimeZone('UTC'));
        $dt->modify("+{$time} minutes");

        //Når den nye regelen går ut på dato.
        $dateExpire = $dt->format('Y-m-d H:i:s');

        $firewall->saveAutoBlockRule(\WtbFirewall\Helpers\IpHelper::getVisitorIP(), $note, $dateExpire);

        return true;
    }

    /**
     * Logfører at vi har blokkert nåværende IP.
     * 
     * @param \WtbFirewall\Firewall\EventType $eventType Årsak til blokkering (for mange requests eller 404 feil).
     * @param string $note
     */
    private function logBlock(\WtbFirewall\Firewall\EventType $eventType, string $note) {
        $nowUTC = \current_time('mysql', true);

        $log = new \WtbFirewall\Firewall\Log();
        $log->saveLogEntry(\WtbFirewall\Helpers\IpHelper::getVisitorIP(), $eventType, $note, $nowUTC);
    }
}
