<?php

namespace WtbFirewall\AutoProtect;

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

/**
 * Hovedklassen for implementering av automatisk beskyttelse.
 */
class AutoProtectMain {

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

    public function Start() {

        add_filter('authenticate', [$this, 'authenticateLogger'], 99, 3);        
    }

    /**
     * Sjekker om autentisering er mislykket eller ikke.
     * 
     * @param type $user
     * @param string $username
     * @param string $password
     * @return type
     */
    public function authenticateLogger($user, ?string $username, ?string $password) {
        require_once WTB_FIREWALL_PATH . 'AutoProtect/Includes.php';
        \WtbFirewall\Config\FirewallConfig::loadOptions();

        if (\WtbFirewall\Config\FirewallConfig::$autoProtectAuth['enabled'] == false || !$this->isRealLogin($username, $password)) {
            return $user;
        }

        if (is_wp_error($user)) {
            //Mislykket autentisering.
            //Sjekk om dette er wp-login, xmlrpc og avgjør om vi skal registrere dette.
            $authType = $this->getAuthType();

            if (($authType == 'wp-login' && \WtbFirewall\Config\FirewallConfig::$autoProtectAuth['wpLogin'] === true) ||
                    ($authType == 'xmlrpc' && \WtbFirewall\Config\FirewallConfig::$autoProtectAuth['XMLRPC'] === true)) {
                //Registrer som mislykket autentisering.
                $autoLog = new \WtbFirewall\AutoProtect\AutoProtectLog();
                $autoLog->authenticateRegisterFailed(\WtbFirewall\Helpers\IpHelper::getVisitorIP(), $authType);

                if ($autoLog->authenticateCheckBlock(\WtbFirewall\Helpers\IpHelper::getVisitorIP())) {
                    //Ip skal blokkeres og logføres.
                    //Men sjekk først at vi ikke allerede har en regel for denne IP addressen.
                    $firewall = new \WtbFirewall\Firewall\Firewall();
                    if (!$firewall->autoRulesExists(\WtbFirewall\Helpers\IpHelper::getVisitorIP())) {
                        $this->authenticateBlock($authType);
                        $this->authenticateLog($authType);
                    }
                }
            }
        } else {
            //Vellykket autentisering.
            //Slett eventuelle registrerte mislykkete autentiseringer for IP.
            $autoLog = new \WtbFirewall\AutoProtect\AutoProtectLog();
            $autoLog->authenticateClear(\WtbFirewall\Helpers\IpHelper::getVisitorIP());
        }

        return $user;
    }

    /**
     * Returnerer om autentisering var wp-login, xmlrpc, rest.
     *      
     * @return string Autentiserings type: 'wp-login', 'xmlrpc', 'rest'.  Dersom ukjent 'unknown'.
     */
    private function getAuthType(): string {
        // REST
        if ((defined('REST_REQUEST') && REST_REQUEST) || (function_exists('wp_doing_rest') && wp_doing_rest())) {
            return 'rest';
        }

        // XML-RPC
        if (defined('XMLRPC_REQUEST') && XMLRPC_REQUEST) {
            return 'xmlrpc';
        }

        // wp-login.php (default web login)
        global $pagenow;
        if (($pagenow ?? '') === 'wp-login.php') {
            return 'wp-login';
        }
        if (isset($_SERVER['REQUEST_URI']) && str_contains($_SERVER['REQUEST_URI'], '/wp-login.php')) {
            return 'wp-login';
        }

        return 'unknown';
    }

    /**
     * Sjekker at autentisering forsøk virkelig stammer fra login med brukernavn/passord.     
     * WP kan trigge autentisering filteret når login form vises.
     * 
     * @param string $username Eventuelt brukernavn for innlogging.
     * @param string $password Eventuelt passord for innlogging.
     * @return bool TRUE login forsøk, FALSE hvis ikke.
     */
    private function isRealLogin(?string $username, ?string $password): bool {        
        $method = $_SERVER['REQUEST_METHOD'] ?? '';

        $postUser = (isset($_POST['log']) && is_string($_POST['log'])) ? trim($_POST['log']) : '';
        $postPass = (isset($_POST['pwd']) && is_string($_POST['pwd'])) ? trim($_POST['pwd']) : '';

        $argUser = is_string($username) ? trim($username) : '';
        $argPass = is_string($password) ? trim($password) : '';

        $hasUser = ($postUser !== '') || ($argUser !== '');
        $hasPass = ($postPass !== '') || ($argPass !== '');

        $isLoginPost = ($method === 'POST' && $hasUser && $hasPass);

        $isXmlrpc = (defined('XMLRPC_REQUEST') && XMLRPC_REQUEST);
        $isXmlrpcAuthAttempt = ($isXmlrpc && $hasUser && $hasPass);

        return $isLoginPost || $isXmlrpcAuthAttempt;
    }

    /**
     * Blokkerer besøkende pga mislykket autentisering.
     * 
     * @param type $authType Autentisering type dette gjelder, 'wp-login', 'xmlrpc'
     */
    private function authenticateBlock($authType) {
        $firewall = new \WtbFirewall\Firewall\Firewall();

        //Beregn hvor lenge til blokkering skal vare.        
        $time = (int) \WtbFirewall\Config\FirewallConfig::$autoProtectAuth['blockTime'];
        $timeUnit = 1;

        if (\WtbFirewall\Config\FirewallConfig::$autoProtectAuth['blockTimeUnit'] == 'hours') {
            $timeUnit = 60;
        } else if (\WtbFirewall\Config\FirewallConfig::$autoProtectAuth['blockTimeUnit'] == 'days') {
            $timeUnit = 24 * 60;
        }

        $time *= $timeUnit;

        $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');

        $ruleNote = 'Too many failed authentication attempts. (' . $authType . ')';

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

    /**
     * Logfører at en besøkende har blitt blokkert pga mislykket autentisering.
     * 
     * @param type $authType Autentisering type dette gjelder, 'wp-login', 'xmlrpc'
     */
    private function authenticateLog($authType) {

        $eventType = null;

        if ($authType == 'wp-login') {
            $eventType = \WtbFirewall\Firewall\EventType::WPLOGIN_AUTH_FAILED;
        } else if ($authType == 'xmlrpc') {
            $eventType = \WtbFirewall\Firewall\EventType::XMLRPC_AUTH_FAILED;
        }

        $note = 'Failed authentication ' . \WtbFirewall\Config\FirewallConfig::$autoProtectAuth['attempts'] . ' times.' .
                ' Blocked for ' . \WtbFirewall\Config\FirewallConfig::$autoProtectAuth['blockTime'] . ' ' .
                \WtbFirewall\Config\FirewallConfig::$autoProtectAuth['blockTimeUnit'] . '.';

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